В лучших традициях TLDR даём короткую последовательность по тому как: установить ClickHouse, проверить доступ, создать таблицу, загрузить публичный dataset, выполнить несколько запросов, снять базовые замеры и проверить backup/restore.
- Установите ClickHouse Server и Client на Ubuntu, запустите сервис и включите автозапуск:
sudo systemctl enable --now clickhouse-serverПроверьте состояние:
sudo systemctl status clickhouse-serverИ подключение:
clickhouse-client- Убедитесь, что сервер отвечает:
SELECT version();Затем создайте отдельную database и таблицу на MergeTree:
CREATE DATABASE demo;
CREATE TABLE demo.trips
(
pickup_datetime DateTime,
passenger_count UInt8,
trip_distance Float32,
fare_amount Float32
)
ENGINE = MergeTree
ORDER BY pickup_datetime;- Подготовьте публичный dataset, проверьте его формат и размер, после чего загрузите данные через
INSERT... FORMAT или подходящий input format.
Например:
clickhouse-client \
--query="INSERT INTO demo.trips FORMAT CSV" \
< trips.csv- После импорта проверьте количество строк:
SELECT count()
FROM demo.trips;И несколько реальных значений:
SELECT *
FROM demo.trips
LIMIT 10;- Выполните несколько аналитических запросов и зафиксируйте результат вместе со временем выполнения.
Например:
SELECT
passenger_count,
count() AS trips,
avg(trip_distance) AS avg_distance
FROM demo.trips
GROUP BY passenger_count
ORDER BY trips DESC;- Перед замерами зафиксируйте конфигурацию VPS:
nproc
free -h
lsblk
df -hТакже сохраните:
- версию ClickHouse;
- размер исходного dataset;
- количество строк;
- тип диска;
- RAM и CPU;
- время импорта;
- время контрольных запросов.
- Настройте доступ так, чтобы ClickHouse не был без необходимости открыт всему интернету. Если нужен удаленный клиент, разрешайте только доверенный адрес через firewall и отдельного пользователя с необходимыми правами.
- Создайте backup тестовой database:
BACKUP DATABASE demo
TO Disk('backups', 'demo-backup');Затем проверьте восстановление через RESTORE на отдельной таблице или после контролируемого удаления тестовых данных.
- При проблемах с импортом сначала проверяйте:
- число столбцов;
- типы данных;
- выбранный FORMAT;
- разделители;
- даты;
NULL;- кодировку;
- достаточно ли RAM и disk space.
Если ClickHouse запускается автоматически, пользователь подключается с нужными правами, dataset импортируется полностью, контрольные запросы возвращают ожидаемые результаты, а backup реально восстанавливается — базовый сервер можно считать готовым к дальнейшей эксплуатации.
Чего ожидать в материале
ClickHouse обычно ставят не ради пустого SELECT 1. Интерес начинается тогда, когда на сервер нужно загрузить нормальный объем данных, быстро посчитать агрегаты и понять, что происходит с CPU, RAM и диском во время импорта и запросов.
Поэтому здесь не ограничимся установкой. Возьмем общедоступный dataset, создадим под него таблицу, загрузим данные и сразу проверим несколько запросов с реальными результатами. Заодно зафиксируем конфигурацию VPS и условия замеров, чтобы цифры можно было нормально интерпретировать, а не сравнивать «скорость ClickHouse вообще».
После этого разберем доступ, backup и восстановление, а под конец специально пройдемся по типичным проблемам импорта: неверный format, несовпадение схемы, ошибки типов и нехватка ресурсов.
С планом разобрались — теперь можно собирать ClickHouse-сервер по частям.
Как будет устроен ClickHouse-сервер
Перед установкой полезно зафиксировать общую схему стенда. В нашем случае все будет максимально просто: один VPS с Ubuntu, на нем clickhouse-server, а подключаться к базе будем через clickhouse-client. После установки создадим отдельную database, таблицу на MergeTree, загрузим публичный dataset и уже на нем проверим импорт, запросы, использование ресурсов и backup.
То есть сначала собираем минимальный standalone-сервер, а уже затем постепенно добавляем данные и эксплуатационные проверки.
Что установим и проверим
На VPS понадобятся два основных компонента:
clickhouse-server
clickhouse-clientclickhouse-server отвечает за хранение данных и выполнение запросов. clickhouse-client — консольный клиент, через который удобно подключаться к серверу, выполнять SQL и проверять результат.
В процессе статьи последовательно проверим:
- Запуск ClickHouse через systemd;
- Локальное подключение;
- Отдельного пользователя и права;
- Доступ по сети;
- Создание database и таблицы;
- Импорт публичного dataset;
- Количество и структуру загруженных строк;
- Скорость нескольких запросов;
- Использование диска;
- Backup и restore;
- Поведение после reboot;
- Типичные ошибки импорта.
Так мы получим не просто установленный пакет, а небольшой стенд, на котором можно проверить весь базовый жизненный цикл данных.
Как ClickHouse хранит и обрабатывает данные
ClickHouse относится к column-oriented СУБД. В отличие от классической row-oriented database вроде MySQL, где значения одной строки физически удобно читать вместе, здесь данные организованы вокруг отдельных столбцов.
Упрощенно обычную таблицу можно представить так:
row 1 → time, city, distance, price
row 2 → time, city, distance, price
row 3 → time, city, distance, price
А column-oriented подход — так:

Это особенно полезно для аналитических запросов, которые читают несколько нужных столбцов из миллионов строк.
Например:
SELECT
passenger_count,
avg(trip_distance)
FROM trips
GROUP BY passenger_count;ClickHouse не нужно обрабатывать все остальные поля таблицы только потому, что они находятся рядом со строкой.
Для нашей таблицы будем использовать семейство MergeTree. Оно дает привычную для ClickHouse модель хранения с parts, сортировкой данных и эффективным чтением больших наборов.
Теперь, когда понятен сам сервер, разберемся, как до него будет добираться клиент.
Как клиент подключается к ClickHouse
На самом VPS проще всего использовать:
clickhouse-clientОн устанавливает соединение с ClickHouse Server и открывает интерактивную SQL-сессию.
Базовая схема:

Для локальной проверки приложение и ClickHouse могут находиться на одном сервере.
В production архитектура часто выглядит иначе:

В таком случае доступ нужно отдельно ограничить через listen settings, пользователя и firewall. Этим займемся после базовой установки.
Но прежде нужен реальный dataset, иначе проверять ClickHouse будет практически не на чем.
Какой dataset будем загружать
Для практики возьмем публичные NYC Yellow Taxi Trip Records.
Это удобный вариант для первого теста ClickHouse: dataset содержит даты поездок, расстояние, количество пассажиров, стоимость и другие поля, поэтому на нем можно показать и обычную выборку, и фильтрацию, и несколько нормальных аналитических агрегатов.
Чтобы инструкция оставалась воспроизводимой даже на небольшом VPS, будем работать не со всей историей поездок, а с одним фиксированным месячным файлом.
Исходный dataset доступен в формате Parquet, поэтому дополнительно получится показать, как ClickHouse работает с современным columnar file format.
Перед импортом обязательно зафиксируем:
- Конкретный файл;
- Его размер;
- Количество строк;
- Список используемых столбцов;
- Формат данных.
Это понадобится не только для импорта, но и для корректных замеров.
Что будем измерять
На тестовом сервере нас интересует не абстрактное утверждение, что ClickHouse работает быстро, а конкретные показатели при конкретной конфигурации.
Зафиксируем: CPU, RAM, disk, версию ClickHouse, размер исходного dataset, количество строк
После этого измерим:
- Время импорта;
- Скорость загрузки строк;
- Время простого
SELECT; - Время агрегирующего запроса;
- Повторный запуск того же запроса;
- Объем таблицы на диске.
Итоговую схему можно свести так:
| Компонент | Назначение | Что проверим |
clickhouse-server | Хранение и обработка данных | Service, version, reboot |
clickhouse-client | SQL-подключение | Login и запросы |
MergeTree | Хранение таблицы | Schema, rows, parts |
| NYC Taxi dataset | Тестовые данные | Import и аналитика |
| Disk | Хранение data parts | Размер и свободное место |
| CPU / RAM | Выполнение запросов | Нагрузку и условия теста |
| Backup | Восстановление данных | BACKUP и RESTORE |
Архитектура получилась небольшой, но для первого знакомства с ClickHouse этого достаточно. Теперь нужно выбрать VPS, на котором такой стенд не будет упираться в ресурсы уже на этапе импорта.
Выбираем версию ClickHouse и конфигурацию VPS
Какие ресурсы нужны небольшому ClickHouse
Для небольшого тестового сервера можно начать примерно с:
2 vCPU
4 GB RAM
40–60 GB SSD
Такой конфигурации достаточно для знакомства с ClickHouse, небольших datasets и базовых запросов.
Более комфортный вариант:
4 vCPU
8 GB RAM
80+ GB SSD/NVMe
Он дает больше запаса для импорта, фоновых merge и аналитических запросов.
Особенно важно не рассчитывать disk только под конечный размер таблицы. ClickHouse дополнительно использует пространство для data parts, merge, временных файлов и backup.
Почему ClickHouse важны CPU, RAM и быстрый диск
CPU используется при parsing, compression, decompression, filtering, aggregation, sorting и выполнении функций. Поэтому аналитические запросы обычно хорошо чувствуют количество доступных vCPU.
RAM нужна для промежуточных данных: aggregation states, sorting, buffers, joins и других операций.
При этом весь dataset не обязан помещаться в оперативную память. Основные данные находятся на disk, а RAM используется для выполнения запросов и cache.
Storage влияет сразу на несколько операций:
- import → запись данных
SELECT→ чтение столбцовMergeTree→ merge parts- backup → чтение и запись
Поэтому для ClickHouse предпочтительнее использовать SSD или NVMe.
Как объем данных влияет на конфигурацию
Размер исходного файла и размер таблицы ClickHouse — не одно и то же.
После импорта данные:
- Преобразуются к типам ClickHouse;
- Разбиваются на parts;
- Сортируются согласно
ORDER BY; - Сжимаются;
- Получают служебные metadata.
При этом дополнительное место требуется и во время эксплуатации.
Условно disk должен вмещать: данные + временные файлы + merge + backup + свободный запас
С RAM ситуация похожая. Даже если сам dataset занимает десятки гигабайт, это не означает, что ClickHouse нужно столько же RAM. Но тяжелые GROUP BY, ORDER BY и JOIN могут потреблять значительно больше памяти, чем простой SELECT count().
Поэтому конфигурацию лучше выбирать не только по размеру dataset, но и по типу запросов.
Какую конфигурацию используем
В нашем случае сервер будет выглядеть так:
Ubuntu 24.04 LTS
4 vCPU
8 GB RAM
80 GB SSD/NVMe
ClickHouse: Stable
Версию ClickHouse можно проверить после установки:
SELECT version();Для небольшого standalone-сервера такой конфигурации достаточно, чтобы загрузить один месячный dataset NYC Taxi, выполнить базовые аналитические запросы и проверить backup.
Сравнение можно представить так:
| Параметр | Минимальная конфигурация | Рекомендуемая конфигурация | Конфигурация статьи |
| CPU | 2 vCPU | 4 vCPU | 4 vCPU |
| RAM | 4 GB | 8 GB | 8 GB |
| Disk | 40 GB SSD | 80+ GB SSD/NVMe | 80 GB SSD/NVMe |
| OS | Ubuntu 24.04 LTS | Ubuntu 24.04 LTS | Ubuntu 24.04 LTS |
| ClickHouse | Stable | Stable | stable release |
Теперь можно зафиксировать условия, при которых будут выполняться импорт и запросы.
Фиксируем условия будущих замеров
Результаты ClickHouse имеют смысл только вместе с характеристиками сервера и параметрами dataset.
Для нашего теста используем:
| Параметр | Значение |
| OS | Ubuntu 24.04 LTS |
| CPU | 4 vCPU |
| RAM | 8 GB |
| Disk | 80 GB SSD/NVMe |
| ClickHouse | Stable release |
| Dataset | NYC Yellow Taxi Trip Records |
| Период dataset | Январь 2024 года |
| Исходный формат | Parquet |
| Размер файла | 50 MB |
| Количество строк | 2 964 624 |
| Нагрузка VPS во время теста | Без сторонних приложений и фоновой нагрузки |
Во время замеров отдельно зафиксируем: время импорта, скорость загрузки строк, время простого SELECT, время агрегирующего запроса и размер таблицы на disk.
Эти показатели позже позволят оценить, как ClickHouse ведет себя именно на выбранной конфигурации.
Команды проверки VPS

Версию Ubuntu:
lsb_release -aКоличество доступных CPU:
nprocБолее подробную информацию о процессоре:
lscpuRAM:
free -hДиски:
lsblkСвободное пространство:
df -hТип block device:
lsblk -o NAME,SIZE,TYPE,ROTA,MOUNTPOINTSЕсли для основного диска указано:
ROTA=0система видит устройство как non-rotational storage.
Также полезно посмотреть текущую нагрузку:
uptimeТеперь конфигурация сервера и условия будущих замеров определены. Следующий шаг — установить ClickHouse Server и Client, запустить сервис и проверить первое подключение.
Устанавливаем ClickHouse на Ubuntu
Какой способ установки используем
Для нашего сервера используем официальный APT repository ClickHouse.
Схема будет такой:

Такой вариант удобнее ручной установки отдельных binaries: пакеты получают стандартные системные каталоги, service unit и понятный механизм обновления.
Следующий шаг — разобраться, что именно появится в системе после установки.
Какие пакеты устанавливаются
Нам понадобятся два основных пакета:
clickhouse-server
clickhouse-clientclickhouse-server запускает саму СУБД и отвечает за хранение данных, выполнение запросов, background merges и другие серверные операции.
clickhouse-client устанавливает консольный клиент для подключения к ClickHouse и выполнения SQL.
Дополнительно пакетный менеджер устанавливает необходимые общие компоненты ClickHouse, поэтому отдельно собирать сервер или загружать standalone binary не понадобится.
После установки сервер работает как отдельный systemd service.
Как работает clickhouse-server
Основной процесс:
clickhouse-serverзапускается в фоне и принимает подключения клиентов.
Его состояние можно проверить через:
sudo systemctl status clickhouse-serverЕсли сервис остановлен:
sudo systemctl start clickhouse-serverДля автоматического запуска после reboot:
sudo systemctl enable clickhouse-serverВ дальнейшем почти все административные проверки будут строиться вокруг двух уровней:
- systemd → работает ли сам процесс
- SQL → отвечает ли ClickHouse и выполняет ли запросы
Даже если systemd показывает active, полезно отдельно выполнить SQL-запрос и убедиться, что сервер действительно принимает соединения.
Для этого понадобится clickhouse-client.
Для чего нужен clickhouse-client
clickhouse-client — консольный SQL client для ClickHouse.
При локальном запуске достаточно выполнить:
clickhouse-clientПосле подключения появляется интерактивная сессия, в которой можно выполнять:
SELECT 1;
SHOW DATABASES;
SELECT version();Также client умеет выполнять отдельный запрос без входа в интерактивный режим:
clickhouse-client --query "SELECT 1"Позже через него же будем создавать database, таблицу, импортировать данные и проверять права пользователей.
Но сначала полезно понимать, где ClickHouse хранит основные файлы.
Где находятся конфигурация, данные и логи
При пакетной установке основные пути разделены по назначению.
Главный каталог конфигурации:
/etc/clickhouse-server/Основной конфигурационный файл:
/etc/clickhouse-server/config.xmlДля дополнительных server settings обычно используются отдельные файлы в:
/etc/clickhouse-server/config.d/Пользовательские настройки можно хранить отдельно от основного файла конфигурации, что удобнее для дальнейшего сопровождения.
Данные ClickHouse располагаются в:
/var/lib/clickhouse/А стандартный каталог логов:
/var/log/clickhouse-server/Например, там могут находиться:
clickhouse-server.log
clickhouse-server.err.logТакое разделение пригодится позже при диагностике: ошибки запуска ищем через systemd и logs, настройки — в /etc/clickhouse-server, а фактические данные — в /var/lib/clickhouse.
Теперь можно установить сервер.
Практика: устанавливаем и запускаем ClickHouse

Сначала устанавливаем пакеты, необходимые для работы HTTPS repository и ключа подписи:
sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl gnupgДобавляем ключ repository:
curl -fsSL \
https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key \
| sudo gpg --dearmor \
-o /usr/share/keyrings/clickhouse-keyring.gpgОпределяем архитектуру:
ARCH=$(dpkg --print-architecture)Добавляем stable repository:
echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg arch=${ARCH}] https://packages.clickhouse.com/deb stable main" \
| sudo tee /etc/apt/sources.list.d/clickhouse.listОбновляем metadata:
sudo apt updateУстанавливаем сервер и client:
sudo apt install -y clickhouse-server clickhouse-clientВо время установки пакет может запросить пароль для пользователя default. Если пароль задается на этом этапе, используем надежное значение и сохраняем его отдельно.
Запускаем сервер:
sudo systemctl start clickhouse-serverИ включаем автозапуск:
sudo systemctl enable clickhouse-serverПроверяем:
sudo systemctl status clickhouse-serverКороткий вариант:
sudo systemctl is-active clickhouse-serverОжидаем:
activeТеперь подключаемся:
clickhouse-clientЕсли для default задан password:
clickhouse-client --passwordПосле успешного подключения можно выполнить:
SELECT 1;Ожидаемый результат:
1Основные команды этого этапа:
| Задача | Команда |
| Установить ClickHouse | sudo apt install -y clickhouse-server clickhouse-client |
| Запустить server | sudo systemctl start clickhouse-server |
| Включить autostart | sudo systemctl enable clickhouse-server |
| Проверить service | sudo systemctl status clickhouse-server |
| Подключиться | clickhouse-client |
| Выполнить SQL без interactive session | clickhouse-client --query "SELECT 1" |
Сервер установлен и принимает соединения.
Проверяем базовую работу ClickHouse
Сам факт запуска systemd service еще не означает, что весь SQL layer работает корректно. Поэтому сразу после установки полезно пройти несколько коротких проверок: подключиться, узнать версию, выполнить запрос и посмотреть доступные databases.
Как подключиться через clickhouse-client
Для локального подключения:
clickhouse-clientЕсли требуется password:
clickhouse-client --passwordМожно явно указать пользователя:
clickhouse-client \
--user default \
--passwordПосле успешного подключения появится prompt ClickHouse, и можно выполнять SQL.
Если нужен всего один запрос, interactive session не обязательна:
clickhouse-client --query "SELECT 1"Это удобно для scripts, diagnostics и автоматических проверок.
Теперь зафиксируем фактическую версию сервера.
Смотрим версию сервера
Версию работающего ClickHouse можно проверить SQL-запросом:
SELECT version();Для условий тестирования важна именно версия server, потому что производительность и поведение отдельных функций могут меняться между releases.
Версию client при необходимости можно посмотреть отдельно:
clickhouse-client --versionServer и client обычно устанавливаются вместе, но для дальнейших проверок ориентируемся прежде всего на результат SELECT version().
Следующим шагом проверим обычный SQL-запрос.
Выполняем первый SELECT
Самая простая проверка:
SELECT 1;Ожидаем:
1Можно сразу проверить несколько функций:
SELECT
now() AS current_time,
hostname() AS server,
version() AS clickhouse_version;Так одним запросом можно проверить, что ClickHouse выполняет SQL, видит системное время и возвращает информацию о сервере.
После этого посмотрим, какие databases доступны сразу после установки.
Как посмотреть databases и tables
Список databases:
SHOW DATABASES;Среди них будут системные databases, необходимые самому ClickHouse.
Таблицы текущей database:
SHOW TABLES;Можно также явно указать database:
SHOW TABLES FROM system;Более подробный вариант через системные таблицы:
SELECT
database,
name,
engine
FROM system.tables
ORDER BY database, name;system database особенно полезна для диагностики. В ней ClickHouse хранит информацию о tables, queries, parts, settings, metrics и других внутренних объектах.
Теперь остается проверить состояние сервера уже средствами самого ClickHouse.
Проверка состояния сервера
Для быстрой SQL-проверки достаточно:
clickhouse-client --query "SELECT 1"Если команда возвращает:
1сервер принимает native client connections и выполняет запросы.
На уровне systemd:
sudo systemctl is-active clickhouse-serverДля просмотра последних сообщений сервиса:
sudo journalctl \
-u clickhouse-server \
-n 50 \
--no-pagerА если нужно проверить процессы:
ps aux | grep '[c]lickhouse-server'Получается несколько уровней диагностики:
| Задача | Команда | Ожидаемый результат |
| Проверить service | systemctl is-active clickhouse-server | active |
| Проверить SQL | clickhouse-client --query "SELECT 1" | 1 |
| Узнать version | SELECT version(); | Версия работающего ClickHouse Server |
| Посмотреть databases | SHOW DATABASES; | Список databases |
| Посмотреть tables | SHOW TABLES; | Список таблиц текущей database |
| Проверить logs | journalctl -u clickhouse-server | Нет критических ошибок запуска |
Теперь ClickHouse не только установлен, но и проверен с двух сторон: systemd подтверждает работу server process, а clickhouse-client успешно выполняет SQL. Следующим шагом можно переходить к сети и authentication — ограничим доступ к серверу и подготовим отдельного пользователя вместо постоянной работы через default.
Настраиваем безопасный доступ
Почему ClickHouse не стоит бездумно открывать в интернет
ClickHouse обычно работает с большими объемами данных и умеет выполнять тяжелые запросы. Если оставить сервер доступным извне без ограничений, проблема будет не только в риске несанкционированного доступа.
Посторонний клиент может:
- Пытаться подобрать credentials;
- Создавать нагрузку запросами;
- Сканировать доступные databases и tables;
- Использовать ошибочно выданные права;
- Создавать лишнюю нагрузку на CPU, RAM и disk.
Поэтому доступ лучше строить слоями:

Даже надежный password не отменяет необходимость ограничивать сам сетевой доступ.
Следующий шаг — понять, какие порты вообще использует ClickHouse.
Какие порты использует ClickHouse
Для обычной работы чаще всего встречаются два основных интерфейса.
Native protocol:
9000/TCPЧерез него обычно подключается:
clickhouse-clientHTTP interface:
8123/TCPОн используется HTTP-клиентами, некоторыми integrations и приложениями.
Также ClickHouse может использовать дополнительные ports для HTTPS, inter-server communication и других задач, но для нашего standalone-сервера на первом этапе достаточно сосредоточиться на 9000 и 8123.
Проверить listening sockets можно так:
sudo ss -lntp | grep clickhouseЕсли сервер слушает только loopback, внешний клиент до этих портов не доберется.
Именно такой вариант подойдет, если приложение находится на том же VPS.
Когда достаточно localhost
Если ClickHouse и приложение работают на одном сервере, самый простой вариант — вообще не открывать database наружу.
Схема:

В таком случае внешний firewall почти не участвует в защите самого ClickHouse, потому что service не принимает подключения на публичном interface.
Это удобно для:
- Небольших внутренних сервисов;
- Тестовых стендов;
- Приложений на одном VPS;
- Сценариев, где удаленный SQL-доступ не нужен.
Если же приложение находится отдельно, localhost уже недостаточно.
Когда нужен удаленный доступ
Удаленное подключение понадобится, если ClickHouse используется другим VPS, BI-инструментом, ETL pipeline или внешним application server.
Предпочтительная схема:

То есть ClickHouse слушает private IP, а firewall разрешает соединения только от нужных hosts.
Если private network недоступна, можно ограничить доступ конкретным публичным source IP.
Например:

Все остальные адреса при этом не должны иметь доступа к порту.
Теперь настроим firewall.
Как ограничить соединения через firewall
Если на VPS используется UFW, сначала проверим состояние:
sudo ufw statusДля локального ClickHouse никаких правил на 9000 или 8123 добавлять не нужно.
Если нужен native access только с одного доверенного IP:
sudo ufw allow from 203.0.113.25 to any port 9000 proto tcpДля HTTP interface:
sudo ufw allow from 203.0.113.25 to any port 8123 proto tcpПроверяем:
sudo ufw status numberedВ результате должны существовать только точечные разрешения, а не:
9000 ALLOW Anywhere
8123 ALLOW AnywhereНо firewall имеет смысл только вместе с корректным listen_host. Поэтому теперь настроим сам ClickHouse.
Практика: открываем доступ только доверенному адресу
Дополнительные настройки удобнее хранить отдельным XML-файлом, не изменяя основной config.xml.
Создадим:
sudo nano /etc/clickhouse-server/config.d/listen.xmlДля localhost:
<clickhouse>
<listen_host>127.0.0.1</listen_host>
</clickhouse>Если ClickHouse должен принимать подключения по private network, можно указать private IP сервера:
<clickhouse>
<listen_host>127.0.0.1</listen_host>
<listen_host>10.0.0.10</listen_host>
</clickhouse>После изменения перезапускаем service:
sudo systemctl restart clickhouse-serverПроверяем:
sudo systemctl is-active clickhouse-serverИ listening ports:
sudo ss -lntp | grep -E ':9000|:8123'Теперь добавляем UFW rule только для доверенного клиента:
sudo ufw allow from 203.0.113.25 to any port 9000 proto tcpПосле этого с доверенного host проверяем:
nc -vz CLICKHOUSE_IP 9000Ожидаем успешное соединение.
С другого адреса попытка должна завершиться timeout или отказом.
Для нашего руководства схема доступа выглядит так:
| Сценарий | listen_host | Firewall | Когда использовать |
| Localhost | 127.0.0.1 | Порт наружу не открывается | Приложение на том же VPS |
| Private network | Private IP + localhost | Только доверенные private IP | Несколько VPS одной инфраструктуры |
| Public network | Public interface | Только конкретные source IP | Когда private network недоступна |
Сетевой доступ теперь ограничен.
Создаем отдельного пользователя
Работать с ClickHouse постоянно через default неудобно и небезопасно. Для приложения или импорта лучше создать отдельного пользователя и выдать ему только те permissions, которые действительно нужны.
Так проще контролировать доступ и позже менять credentials независимо от других клиентов.
Почему приложение не должно работать с избыточными правами
Если приложение умеет только читать и загружать данные в одну database, ему не нужны administrative permissions на весь ClickHouse.
Избыточные права увеличивают последствия любой ошибки в приложении.
Например, компрометированный account с широкими правами может получить возможность:
- Читать чужие databases;
- Удалять tables;
- Менять users;
- Выполнять administrative operations;
- Создавать тяжелые запросы вне нужного scope.
Поэтому полезно придерживаться простого принципа: one application → one user → minimum required permissions
Для нашего dataset достаточно отдельного пользователя с доступом к тестовой database.
Как устроены пользователи и права ClickHouse
В ClickHouse access control можно настраивать через SQL.
Пользователь создается командой:
CREATE USER ...Права выдаются через:
GRANT ...И при необходимости отзываются:
REVOKE ...Права можно задавать на уровне:
- всего сервера;
- database;
- конкретной table.
Например:
demo.*означает объекты внутри database demo.
Это позволяет не давать приложению доступ ко всем databases сразу.
Теперь определим, какие permissions нужны нашему пользователю.
Какие права нужны для загрузки и чтения данных
Для нашего сценария пользователь должен уметь:
- Подключаться;
- Читать данные;
- Вставлять строки;
- Работать с объектами внутри тестовой database.
После того как database и table будут созданы, базовый набор для работы с данными можно ограничить:
SELECT
INSERTЕсли этот же пользователь будет создавать tables в рамках руководства, временно понадобятся и DDL permissions.
Например:
CREATE
DROPНо в production лучше разделять роли:
deploy/admin user → CREATE / ALTER / DROP
application user → SELECT / INSERT
В нашей работе сначала создадим пользователя для практической работы, а затем права можно будет сузить.
Практика: создаем пользователя и проверяем вход
Подключаемся локально с administrative account:
clickhouse-clientСоздаем пользователя:
CREATE USER app_user
IDENTIFIED WITH sha256_password BY 'StrongPassword123';Создаем database:
CREATE DATABASE IF NOT EXISTS demo;Выдаем права:
GRANT SELECT, INSERT, CREATE TABLE, DROP TABLE
ON demo.*
TO app_user;Проверяем grants:
SHOW GRANTS FOR app_user;Теперь выходим из текущей session и подключаемся под новым пользователем:
clickhouse-client \
--user app_user \
--passwordПосле запроса password вводим:
StrongPassword123Проверяем текущего пользователя:
SELECT currentUser();Ожидаем:
app_userПроверяем доступ к database:
SHOW TABLES FROM demo;Если database пока пустая, ClickHouse просто не вернет tables.
Можно также проверить разрешенный SQL:
SELECT 1;Теперь пользователь готов для дальнейшего импорта.
Проверяем отказ с неправильным паролем

Безопасность лучше проверить не только успешным login, но и заведомо неправильными credentials.
Пробуем:
clickhouse-client \
--user app_user \
--passwordИ вводим неправильный password.
ClickHouse должен отказать в authentication.
После этого снова подключаемся с корректным password:
clickhouse-client \
--user app_user \
--passwordИ выполняем:
SELECT currentUser();Получаем:
app_userДополнительно полезно проверить, что пользователь не получил доступ за пределами нужной database.
Например, если права ограничены demo.*, попытка выполнить административную операцию вне нее должна завершиться ошибкой permissions.
Теперь сетевой доступ ограничен, а для дальнейшей работы есть отдельный пользователь. Следующий шаг — разобраться с databases, таблицами и MergeTree, чтобы правильно подготовить структуру под NYC Taxi dataset.
Разбираемся с databases, tables и MergeTree
Что такое database в ClickHouse
Database в ClickHouse — это логический контейнер для tables, views и других объектов.
Например:

Здесь demo — database, а trips — table внутри нее.
Создается database обычной SQL-командой:
CREATE DATABASE demo;После этого таблицу можно создавать уже внутри нее:
CREATE TABLE demo.trips
(
...
)
ENGINE = MergeTree
ORDER BY ...Разделение на databases удобно, если на одном сервере находятся разные приложения или datasets. Так проще отделять данные, выдавать permissions и выполнять backup отдельных логических групп.
Но сама database почти ничего не говорит о физической структуре хранения. Основные свойства таблицы определяются ее engine.
Почему для обычной таблицы выбираем MergeTree
Для аналитических данных в ClickHouse чаще всего используется семейство MergeTree.
Оно рассчитано на большие объемы строк, быструю вставку блоками и эффективное чтение отсортированных данных.
Упрощенно процесс выглядит так:

Каждая вставка формирует один или несколько parts на диске, а затем ClickHouse постепенно объединяет их в background.
Это позволяет не переписывать всю таблицу при каждой новой загрузке и хорошо подходит для append-heavy аналитических workloads.
Кроме базового MergeTree, существуют его специализированные варианты вроде ReplacingMergeTree, SummingMergeTree и других. Но для первого dataset они нам не нужны — обычного MergeTree достаточно.
Следующий важный параметр такого engine — ключ сортировки.
Зачем нужен ORDER BY
В таблицах семейства MergeTree выражение ORDER BY задает порядок, в котором данные организуются внутри data parts.
Например:
ENGINE = MergeTree
ORDER BY pickup_datetimeозначает, что данные будут физически упорядочиваться по pickup_datetime.
Это особенно полезно для запросов вроде:
SELECT *
FROM demo.trips
WHERE pickup_datetime >= '2026-01-01 00:00:00'
AND pickup_datetime < '2026-01-02 00:00:00';ClickHouse может использовать структуру сортировки, чтобы не читать без необходимости весь dataset.
Поэтому ORDER BY стоит выбирать под наиболее характерные фильтры и диапазоны запросов.
Но здесь есть важный момент: ORDER BY в описании таблицы — не то же самое, что ORDER BY в обычном SELECT.
Чем ORDER BY в ClickHouse отличается от обычной сортировки результата
В запросе:
SELECT *
FROM demo.trips
ORDER BY fare_amount DESC
LIMIT 10;ORDER BY сортирует уже полученный результат перед выдачей клиенту.
В определении таблицы:
ENGINE = MergeTree
ORDER BY pickup_datetimeон задает структуру хранения данных.
То есть это два разных уровня:
CREATETABLE ...ORDER BY→ как данные организованы на дискеSELECT ...ORDER BY→ как отсортировать результат запроса
Можно создать таблицу с:
ORDER BY pickup_datetimeа потом выполнять запросы с:
ORDER BY fare_amount DESCНикакого противоречия здесь нет.
После engine и sorting key остается еще одна важная часть схемы — типы столбцов.
Как типы столбцов влияют на хранение и импорт
ClickHouse строго работает с типами данных.
Например:
DateTimeUInt8UInt16UInt32Float32Float64String- Nullable(...)
Правильно выбранный тип влияет сразу на несколько вещей:
- Размер данных на диске;
- Объем RAM во время обработки;
- Возможность сжатия;
- Скорость запросов;
- Успешность импорта.
Например, если значение всегда находится в диапазоне от 0 до 255, нет смысла хранить его в UInt64.
С другой стороны, слишком узкий тип может привести к ошибке импорта, если в dataset встретится значение вне допустимого диапазона.
Отдельно нужно учитывать NULL.
Если исходные данные могут содержать отсутствующие значения, столбец можно сделать:
Nullable(UInt8)или выбрать другой способ представления missing values.
Основные элементы таблицы можно свести так:
| Элемент | Зачем нужен |
| Database | Логически объединяет tables и другие объекты |
| Table | Хранит конкретный dataset |
MergeTree | Определяет основной механизм хранения и merge |
ORDER BY | Задает порядок данных внутри table parts |
| Column type | Определяет формат, размер и допустимые значения |
| Nullable(...) | Позволяет хранить NULL |
Теперь можно переходить к самому dataset и подбирать schema уже под реальные поля.
Подготавливаем публичный тестовый dataset
Для проверки ClickHouse лучше использовать данные, на которых можно показать не только простой импорт, но и нормальные аналитические запросы. Поэтому возьмем публичный dataset поездок такси Нью-Йорка.
Он достаточно большой, имеет понятную структуру и хорошо подходит для агрегаций по времени, расстоянию, количеству пассажиров и стоимости поездок.
Какой dataset будем использовать
Возьмем NYC Yellow Taxi Trip Records за январь 2024 года.
Исходный файл: yellow_tripdata_2024-01.parquet
Dataset содержит сведения о поездках Yellow Taxi, включая:
- Время посадки;
- Время завершения поездки;
- Количество пассажиров;
- Расстояние;
- Pickup и dropoff zones;
- Способ оплаты;
- Fare;
- Tips;
- Total amount.
Для первого импорта этого более чем достаточно.
Почему он подходит для проверки ClickHouse
У dataset есть несколько удобных свойств.
Во-первых, в нем много строк, поэтому можно увидеть реальную разницу между обычным просмотром данных и аналитическими агрегатами.
Во-вторых, поля разных типов:
- timestamps
- integers
- floating-point values
- categorical values
Это позволяет проверить schema и обработку разных column types.
В-третьих, данные хорошо подходят для типичных аналитических запросов:
- поездки по часам
- средняя дистанция
- средний fare
- распределение по
passenger_count - top pickup zones
- сумма tips
Именно на таких workload ClickHouse раскрывается лучше, чем на одиночных row-by-row операциях.
Следующим шагом определим формат импорта.
Какой формат будем импортировать
NYC Taxi dataset доступен в формате Parquet.
Для ClickHouse это удобный вариант, потому что Parquet тоже column-oriented и сохраняет schema внутри файла.
Это упрощает предварительную проверку структуры и хорошо подходит для больших datasets.
Наш путь будет выглядеть так:

Перед фактическим импортом проверим metadata файла и сопоставим его columns с нашей schema.
Так меньше шансов получить ошибку уже после начала загрузки.
Проверяем размер и структуру исходных данных
После загрузки файла посмотрим его размер:
ls -lh yellow_tripdata_2024-01.parquetПолучим файл размером около:
50MДля более точного размера:
du -h yellow_tripdata_2024-01.parquetСтруктуру Parquet можно посмотреть средствами ClickHouse без предварительного импорта:
DESCRIBE file('yellow_tripdata_2024-01.parquet', Parquet);Результат покажет названия columns и типы, которые ClickHouse определил из metadata файла.
Также можно посмотреть несколько строк напрямую:
SELECT *
FROM file('yellow_tripdata_2024-01.parquet', Parquet)
LIMIT 5;Количество строк:
SELECT count()
FROM file('yellow_tripdata_2024-01.parquet', Parquet);Получим:
2964624Так мы заранее знаем размер dataset, его schema и количество строк еще до создания основной table.
Теперь можно выбрать поля для практической таблицы.
Какие поля понадобятся в таблице
Для статьи нет необходимости переносить абсолютно все columns исходного dataset. Достаточно выбрать поля, которые нужны для импорта и последующих запросов.
Например:
| Поле dataset | Тип ClickHouse | Назначение |
tpep_pickup_datetime | DateTime | Время начала поездки |
tpep_dropoff_datetime | DateTime | Время завершения поездки |
passenger_count | Nullable(UInt8) | Количество пассажиров |
trip_distance | Float64 | Расстояние поездки |
PULocationID | UInt16 | Pickup zone |
DOLocationID | UInt16 | Dropoff zone |
payment_type | UInt8 | Тип оплаты |
fare_amount | Float64 | Базовая стоимость |
tip_amount | Float64 | Tips |
total_amount | Float64 | Итоговая стоимость |
Для passenger_count используем Nullable, потому что в реальном dataset могут встречаться отсутствующие значения.
В качестве sorting key логично взять:
tpep_pickup_datetimeпоскольку многие последующие запросы будут связаны с временными диапазонами.
Получившаяся схема уже достаточно близка к реальному аналитическому workload: есть timestamps, numeric metrics и несколько categorical identifiers.
Создаем database и таблицу
Dataset подготовлен, а его структура уже понятна. Теперь создадим отдельную database и таблицу MergeTree, в которую загрузим выбранные поля NYC Yellow Taxi.
Для первого стенда не будем усложнять схему partitions или специализированными engines. Наша задача — получить понятную таблицу, пригодную для импорта, фильтрации по времени и первых аналитических запросов.
Создаем отдельную database
Для тестовых данных используем database:
demoСоздаем ее:
CREATE DATABASE IF NOT EXISTS demo;Проверяем:
SHOW DATABASES;В списке должна появиться:
demoТеперь все объекты, связанные с NYC Taxi, можно держать отдельно от системных databases.
Следующий шаг — определить структуру самой таблицы.
Определяем структуру таблицы
Из исходного Parquet нам понадобятся не все поля, а только те, которые пригодятся в дальнейших запросах.
Используем такую схему:
CREATE TABLE demo.trips
(
pickup_datetime DateTime,
dropoff_datetime DateTime,
passenger_count Nullable(UInt8),
trip_distance Float64,
pickup_location_id UInt16,
dropoff_location_id UInt16,
payment_type Nullable(UInt8),
fare_amount Float64,
tip_amount Float64,
total_amount Float64
)
ENGINE = MergeTree
ORDER BY (pickup_datetime, pickup_location_id);Здесь мы заодно приводим исходные названия вроде PULocationID к более удобному стилю:
PULocationID→pickup_location_idDOLocationID→dropoff_location_id
passenger_count и payment_type оставляем Nullable, потому что в исходных данных могут встречаться отсутствующие значения.
Числовые суммы и расстояние храним в Float64, а location IDs — в UInt16, поскольку значения помещаются в этот диапазон.
После определения columns остается выбрать sorting key.
Выбираем ORDER BY
Для таблицы используем:
ORDER BY (pickup_datetime, pickup_location_id)Первым идет pickup_datetime, потому что многие аналитические запросы к поездкам естественно ограничиваются периодом:
WHERE pickup_datetime >= '2024-01-01'
AND pickup_datetime < '2024-01-02'Вторым добавляем pickup_location_id. Это поможет, если запрос одновременно фильтрует данные по времени и pickup zone.
Упрощенно порядок хранения будет ориентирован на:

Для нашей учебной таблицы такого sorting key достаточно. Более сложный production workload мог бы потребовать другого порядка в зависимости от реальных запросов.
Теперь создадим таблицу полностью.
Создаем таблицу MergeTree
Подключаемся под пользователем, которому ранее выдали CREATE TABLE:
clickhouse-client \
--user app_user \
--passwordСоздаем table:
CREATE TABLE demo.trips
(
pickup_datetime DateTime,
dropoff_datetime DateTime,
passenger_count Nullable(UInt8),
trip_distance Float64,
pickup_location_id UInt16,
dropoff_location_id UInt16,
payment_type Nullable(UInt8),
fare_amount Float64,
tip_amount Float64,
total_amount Float64
)
ENGINE = MergeTree
ORDER BY (pickup_datetime, pickup_location_id);Проверяем, что она появилась:
SHOW TABLES FROM demo;Ожидаем:
tripsДо импорта таблица должна быть пустой.
Проверим это:
SELECT count()
FROM demo.trips;Получим:
0Теперь остается убедиться, что ClickHouse создал именно ту schema, которую мы ожидали.
Проверяем созданную схему

Самый быстрый вариант:
DESCRIBE TABLE demo.trips;В выводе должны присутствовать наши десять columns:
pickup_datetimedropoff_datetimepassenger_counttrip_distancepickup_location_iddropoff_location_idpayment_typefare_amounttip_amounttotal_amount
Engine и sorting key удобно проверить отдельно:
SHOW CREATE TABLE demo.trips;В конце определения должны быть:
ENGINE = MergeTree
ORDER BY (pickup_datetime, pickup_location_id)Еще раз проверяем число строк:
SELECT count()
FROM demo.trips;Пока:
0Таблица готова. Теперь можно переходить к главной части — загрузить все 2 964 624 поездки из Parquet и проверить, сколько времени и места это заняло.
Загружаем первые данные в ClickHouse
Схема создана, поэтому теперь перенесем данные из yellow_tripdata_2024-01.parquet в demo.trips.
Так как исходный файл содержит больше columns и названия некоторых полей отличаются от нашей таблицы, будем использовать не прямой INSERT ... FORMAT Parquet, а INSERT ... SELECT. Это позволяет выбрать только нужные поля и сразу привести их к нашей schema.
Как работает INSERT в ClickHouse
Для загрузки данных ClickHouse использует обычный SQL:
INSERT INTO table ...Если данные уже представлены другим источником ClickHouse, можно использовать:
INSERT INTO target_table
SELECT ...
FROM source;В нашем случае source — Parquet-файл:

Такой подход удобен еще и тем, что преобразования выполняются непосредственно во время импорта.
Теперь подготовим файл для чтения сервером.
Импортируем публичный dataset
Table function file() читает файлы из разрешенного ClickHouse каталога. Для пакетной установки обычно используется:
/var/lib/clickhouse/user_files/Копируем dataset туда:
sudo cp yellow_tripdata_2024-01.parquet \
/var/lib/clickhouse/user_files/Проверяем:
sudo ls -lh /var/lib/clickhouse/user_files/yellow_tripdata_2024-01.parquetТеперь можно выполнить импорт:
INSERT INTO demo.trips
SELECT
tpep_pickup_datetime AS pickup_datetime,
tpep_dropoff_datetime AS dropoff_datetime,
CAST(passenger_count, 'Nullable(UInt8)') AS passenger_count,
trip_distance,
toUInt16(PULocationID) AS pickup_location_id,
toUInt16(DOLocationID) AS dropoff_location_id,
CAST(payment_type, 'Nullable(UInt8)') AS payment_type,
fare_amount,
tip_amount,
total_amount
FROM file(
'yellow_tripdata_2024-01.parquet',
Parquet
);ClickHouse прочитает Parquet, выберет нужные columns, выполнит преобразования типов и запишет результат в MergeTree.
После завершения client вернет информацию о выполненном запросе, а именно о времени импорта и скорости обработки.
Эти показатели уже относятся к фактическому benchmark нашего VPS.
Дальше проверим, что импорт действительно завершился полностью.
Как отслеживать выполнение импорта
Во время работы запроса clickhouse-client показывает progress: количество обработанных строк, объем прочитанных данных и скорость.
Для дополнительной проверки с другой session можно посмотреть выполняющиеся queries:
SELECT
query_id,
elapsed,
read_rows,
read_bytes,
memory_usage,
query
FROM system.processes;Если импорт еще работает, его запрос будет виден в system.processes.
После завершения он исчезнет из списка активных queries.
Историю выполненных запросов можно посмотреть через system.query_log, если query logging включен:
SELECT
query_duration_ms,
read_rows,
read_bytes,
written_rows,
written_bytes,
memory_usage
FROM system.query_log
WHERE query LIKE 'INSERT INTO demo.trips%'
AND type = 'QueryFinish'
ORDER BY event_time DESC
LIMIT 1;Полученные показатели пригодятся позже, когда будем отдельно фиксировать скорость импорта.
Но сначала нужно проверить самое главное — число строк в таблице.
Проверяем количество загруженных строк
В исходном файле:
2 964 624 строки
После импорта выполняем:
SELECT count()
FROM demo.trips;Ожидаем:
2964624Это важная контрольная точка: количество строк в demo.trips должно совпасть с количеством записей исходного Parquet, поскольку при импорте мы выбирали columns, но не фильтровали сами строки.
Можно сразу посмотреть несколько записей:
SELECT *
FROM demo.trips
LIMIT 5;И проверить диапазон дат:
SELECT
min(pickup_datetime) AS min_time,
max(pickup_datetime) AS max_time
FROM demo.trips;Так мы убеждаемся, что таблица не только содержит правильное количество строк, но и действительно получила данные из NYC Taxi dataset.
Осталось посмотреть, сколько места они заняли после преобразования в MergeTree.
Смотрим объем данных на диске

Размер таблицы можно получить из системной таблицы system.parts.
Выполняем:
SELECT
formatReadableQuantity(sum(rows)) AS rows,
formatReadableSize(sum(bytes_on_disk)) AS disk_size,
formatReadableSize(sum(data_compressed_bytes)) AS compressed_size,
formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed_size
FROM system.parts
WHERE database = 'demo'
AND table = 'trips'
AND active;Размеры здесь уже зависят от реально созданных parts, версии ClickHouse и compression, поэтому их лучше взять непосредственно со стенда.
Количество активных parts:
SELECT count()
FROM system.parts
WHERE database = 'demo'
AND table = 'trips'
AND active;На уровне файловой системы можно дополнительно посмотреть каталог ClickHouse:
sudo du -sh /var/lib/clickhouse/Но для размера конкретной таблицы system.parts удобнее и точнее, поскольку ClickHouse сам показывает данные выбранного объекта.
Первый полноценный dataset теперь находится в ClickHouse: все 2 964 624 строк загружены в MergeTree, количество записей проверено, а фактический размер таблицы можно сравнить с исходным Parquet.
Проверяем загруженные данные
Импорт завершен, но одного совпадения количества строк недостаточно. Перед аналитическими запросами полезно проверить сами данные: посмотреть несколько записей, диапазоны дат и числовых значений, NULL, а также фактический размер таблицы.
Так мы сразу увидим, что dataset действительно загрузился корректно и какие особенности исходных данных нужно учитывать дальше.
Выводим первые строки
Начнем с обычной выборки:
SELECT *
FROM demo.trips
LIMIT 5;Первые записи будут выглядеть примерно так:

Такой просмотр удобен для быстрой проверки соответствия столбцов: timestamps выглядят как timestamps, location IDs — как числовые identifiers, а суммы и расстояния не «съехали» в соседние columns.
После этого стоит посмотреть уже не отдельные строки, а диапазоны значений.
Проверяем диапазон значений
Для timestamps выполним:
SELECT
min(pickup_datetime) AS min_pickup,
max(pickup_datetime) AS max_pickup,
min(dropoff_datetime) AS min_dropoff,
max(dropoff_datetime) AS max_dropoff
FROM demo.trips;У январского файла есть небольшая особенность: формально это dataset за январь 2024 года, но в нем встречаются единичные timestamps за пределами месяца. Минимальный pickup_datetime — 2002-12-31 22:59:39, а максимальный — 2024-02-01 00:01:15. Таких строк очень мало, поэтому для аналитики именно января дальше будем явно ограничивать период через WHERE.
Теперь посмотрим диапазоны числовых показателей:
SELECT
min(trip_distance) AS min_distance,
max(trip_distance) AS max_distance,
min(fare_amount) AS min_fare,
max(fare_amount) AS max_fare,
min(total_amount) AS min_total,
max(total_amount) AS max_total
FROM demo.trips;Здесь уже могут встретиться отрицательные суммы, нулевые расстояния и другие выбросы. Для публичного NYC Taxi dataset это нормально: сырые записи не стоит воспринимать как полностью очищенный набор данных. Именно поэтому в аналитических запросах иногда понадобится дополнительная фильтрация.
Переходим к контрольному количеству строк.
Считаем строки
После полного импорта:
SELECT count()
FROM demo.trips;получаем:
2964624Это совпадает с количеством записей в исходном yellow_tripdata_2024-01.parquet. DataKitchen
Дополнительно можно проверить количество поездок непосредственно за январь 2024 года:
SELECT count()
FROM demo.trips
WHERE pickup_datetime >= '2024-01-01 00:00:00'
AND pickup_datetime < '2024-02-01 00:00:00';Поскольку в исходном файле есть 18 строк с pickup_datetime вне января, результат будет:
2964606Такой фильтр дальше пригодится для запросов, где нам нужна именно статистика за календарный январь.
Теперь посмотрим, насколько dataset чистый.
Ищем NULL и некорректные значения
Сначала проверим nullable columns:
SELECT
countIf(passenger_count IS NULL) AS passenger_count_null,
countIf(payment_type IS NULL) AS payment_type_null
FROM demo.trips;Кроме NULL, имеет смысл проверить очевидные аномалии:
SELECT
countIf(passenger_count IS NULL OR passenger_count < 1) AS invalid_passenger_count,
countIf(trip_distance <= 0 OR trip_distance > 1000) AS invalid_distance,
countIf(fare_amount < 0) AS negative_fare,
countIf(total_amount < 0) AS negative_total,
countIf(dropoff_datetime < pickup_datetime) AS invalid_time
FROM demo.trips;Публичный dataset действительно содержит подобные записи: встречаются поездки без указанного числа пассажиров, нулевые или аномальные расстояния, отрицательные суммы и отдельные случаи, где dropoff находится раньше pickup. DataKitchen
Это важный момент: успешный импорт не означает, что все исходные значения бизнес-логически корректны.
Поэтому дальше при расчете показателей будем либо явно работать со всем raw dataset, либо добавлять условия вроде:
WHERE trip_distance > 0
AND fare_amount >= 0в зависимости от задачи.
После проверки содержимого остается посмотреть физический размер таблицы.
Проверяем размер таблицы
Используем system.parts:
SELECT
sum(rows) AS rows,
formatReadableSize(sum(bytes_on_disk)) AS disk_size,
formatReadableSize(sum(data_compressed_bytes)) AS compressed_size,
formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed_size
FROM system.parts
WHERE database = 'demo'
AND table = 'trips'
AND active;Количество строк здесь должно снова быть: 2964624
Для быстрого контроля основные запросы можно собрать в одну таблицу:
| Что проверяем | Запрос | Ожидаемый результат |
| Первые записи | SELECT * FROM demo.trips LIMIT 5 | Данные читаются |
| Все строки | SELECT count() FROM demo.trips | 2964624 |
| Только январь 2024 | WHERE pickup_datetime >= ... AND < ... | 2964606 |
| Диапазон дат | min() / max() | Видны редкие outliers |
NULL и аномалии | countIf(...) | Можно оценить качество данных |
| Размер таблицы | system.parts | Фактический размер MergeTree |
Теперь мы знаем, что таблица загружена полностью, и понимаем ограничения исходного dataset. Можно переходить к тому, ради чего ClickHouse обычно и используют, — аналитическим запросам.
Выполняем первые аналитические запросы
Начнем с простых агрегатов, а затем постепенно добавим GROUP BY, сортировку, фильтрацию и более тяжелую агрегацию сразу по нескольким dimensions.
Чтобы статистика относилась именно к январю 2024 года, в запросах по времени будем исключать несколько строк, случайно попавших за границы месяца.
Считаем агрегаты по всему dataset
Для начала посмотрим основные показатели:
SELECT
count() AS trips,
round(avg(trip_distance), 2) AS avg_distance,
round(avg(fare_amount), 2) AS avg_fare,
round(avg(tip_amount), 2) AS avg_tip,
round(avg(total_amount), 2) AS avg_total
FROM demo.trips
WHERE pickup_datetime >= '2024-01-01 00:00:00'
AND pickup_datetime < '2024-02-01 00:00:00';Такой запрос сканирует несколько numeric columns почти по трем миллионам строк и сразу возвращает общую статистику по месяцу.
Если нужны только заведомо валидные поездки, можно дополнительно исключить очевидные аномалии:
AND trip_distance > 0
AND fare_amount >= 0
AND total_amount >= 0Но для первого теста оставим raw dataset и будем помнить, что он содержит выбросы.
Теперь сгруппируем поездки.
Группируем данные
Посчитаем количество поездок по числу пассажиров:
SELECT
passenger_count,
count() AS trips
FROM demo.trips
WHERE pickup_datetime >= '2024-01-01 00:00:00'
AND pickup_datetime < '2024-02-01 00:00:00'
GROUP BY passenger_count
ORDER BY trips DESC;Такой запрос хорошо показывает базовую механику аналитики ClickHouse: read rows → group by passenger_count → count() → sort result
В dataset встречаются не только обычные значения вроде 1 или 2, но и NULL и нулевой passenger_count, поэтому группировка заодно показывает особенности качества исходных данных.
Следующим шагом добавим сортировку уже по агрегированному показателю.
Сортируем результат
Например, найдем pickup zones с наибольшим количеством поездок:
SELECT
pickup_location_id,
count() AS trips
FROM demo.trips
WHERE pickup_datetime >= '2024-01-01 00:00:00'
AND pickup_datetime < '2024-02-01 00:00:00'
GROUP BY pickup_location_id
ORDER BY trips DESC
LIMIT 10;Здесь ORDER BY trips DESC уже относится только к результату запроса и никак не меняет sorting key самой таблицы.
Можно усложнить запрос и одновременно посмотреть среднюю стоимость:
SELECT
pickup_location_id,
count() AS trips,
round(avg(total_amount), 2) AS avg_total
FROM demo.trips
WHERE pickup_datetime >= '2024-01-01 00:00:00'
AND pickup_datetime < '2024-02-01 00:00:00'
GROUP BY pickup_location_id
ORDER BY trips DESC
LIMIT 10;Теперь применим более предметную фильтрацию.
Фильтруем данные
Например, выберем только длинные поездки:
SELECT
pickup_datetime,
pickup_location_id,
dropoff_location_id,
trip_distance,
fare_amount,
tip_amount,
total_amount
FROM demo.trips
WHERE pickup_datetime >= '2024-01-01 00:00:00'
AND pickup_datetime < '2024-02-01 00:00:00'
AND trip_distance >= 20
AND fare_amount >= 0
ORDER BY trip_distance DESC
LIMIT 20;Так мы одновременно используем:
- фильтр по sorting key;
- дополнительное условие по
trip_distance; - сортировку результата;
- LIMIT.
Для ClickHouse это уже гораздо ближе к нормальному аналитическому запросу, чем простой SELECT *.
Осталось собрать несколько dimensions в одном более тяжелом запросе.
Выполняем более тяжелый агрегирующий запрос

Посмотрим статистику поездок по часам и типу оплаты:
SELECT
toHour(pickup_datetime) AS hour,
payment_type,
count() AS trips,
round(avg(trip_distance), 2) AS avg_distance,
round(avg(fare_amount), 2) AS avg_fare,
round(avg(tip_amount), 2) AS avg_tip,
round(sum(total_amount), 2) AS total_revenue
FROM demo.trips
WHERE pickup_datetime >= '2024-01-01 00:00:00'
AND pickup_datetime < '2024-02-01 00:00:00'
GROUP BY
hour,
payment_type
ORDER BY
hour,
trips DESC;Здесь ClickHouse за один проход должен:

Именно такой запрос удобно использовать дальше для замеров производительности.
После выполнения clickhouse-client покажет количество обработанных строк, объем данных и время запроса.
Сам SQL и dataset у всех одинаковые, но продолжительность выполнения уже зависит от CPU, RAM, storage, версии ClickHouse и состояния cache. Поэтому фактическое время этого запроса зафиксируем в следующем разделе вместе с условиями benchmark.
Измеряем скорость импорта и запросов
Теперь у нас есть фиксированный dataset и несколько контрольных SQL-запросов, поэтому можно перейти к замерам. Здесь важно сравнивать не абстрактную «скорость ClickHouse», а конкретный workload на конкретном VPS.
Для нашего теста используем одну и ту же таблицу demo.trips, один и тот же файл yellow_tripdata_2024-01.parquet и не запускаем параллельно другие тяжелые задачи.
Какие условия фиксируем перед замером
Проверить основные ресурсы можно командами:
nproc
free -h
lsblk
df -h
uptimeВерсию ClickHouse:
SELECT version();Во время теста желательно выполнять операции последовательно. Иначе фоновый backup, другой тяжелый запрос или активный merge может заметно изменить результат.
С условиями определились — сначала измерим самую тяжелую операцию из уже выполненных, то есть импорт dataset.
Измеряем время загрузки dataset
Для повторного теста сначала очищаем таблицу:
TRUNCATE TABLE demo.trips;После этого снова запускаем импорт:
INSERT INTO demo.trips
SELECT
tpep_pickup_datetime AS pickup_datetime,
tpep_dropoff_datetime AS dropoff_datetime,
CAST(passenger_count, 'Nullable(UInt8)') AS passenger_count,
trip_distance,
toUInt16(PULocationID) AS pickup_location_id,
toUInt16(DOLocationID) AS dropoff_location_id,
CAST(payment_type, 'Nullable(UInt8)') AS payment_type,
fare_amount,
tip_amount,
total_amount
FROM file(
'yellow_tripdata_2024-01.parquet',
Parquet
);После завершения clickhouse-client показывает количество обработанных строк, время выполнения и скорость загрузки. Эти значения можно использовать как фактический результат импорта на текущем VPS.
Дополнительно сведения о запросе можно получить из system.query_log:
SELECT
query_duration_ms,
read_rows,
read_bytes,
written_rows,
written_bytes,
memory_usage
FROM system.query_log
WHERE query LIKE 'INSERT INTO demo.trips%'
AND type = 'QueryFinish'
ORDER BY event_time DESC
LIMIT 1;Так можно получить параметры завершенного импорта напрямую из ClickHouse.
После повторной загрузки проверяем количество строк:
SELECT count()
FROM demo.trips;Ожидаемый результат:
2964624Если количество совпало с исходным dataset, импорт завершился полностью.
Теперь можно переходить к замерам чтения данных.
Измеряем скорость простого SELECT
В качестве простой контрольной операции используем:
SELECT count()
FROM demo.trips;Запрос обрабатывает всю таблицу, но выполняет минимальную агрегацию. После выполнения clickhouse-client покажет количество обработанных строк, время запроса и скорость обработки.
Для более предметного теста добавим фильтр по sorting key:
SELECT count()
FROM demo.trips
WHERE pickup_datetime >= '2024-01-15 00:00:00'
AND pickup_datetime < '2024-01-16 00:00:00';Так можно увидеть, как ClickHouse работает с диапазоном по pickup_datetime, который стоит первым в нашем ORDER BY.
Простой count() дает базовую точку отсчета. Теперь повторим тест с запросом, где нужно читать несколько columns и строить aggregation states.
Измеряем агрегирующий запрос
Используем тот же запрос, который подготовили в предыдущем разделе:
SELECT
toHour(pickup_datetime) AS hour,
payment_type,
count() AS trips,
round(avg(trip_distance), 2) AS avg_distance,
round(avg(fare_amount), 2) AS avg_fare,
round(avg(tip_amount), 2) AS avg_tip,
round(sum(total_amount), 2) AS total_revenue
FROM demo.trips
WHERE pickup_datetime >= '2024-01-01 00:00:00'
AND pickup_datetime < '2024-02-01 00:00:00'
GROUP BY
hour,
payment_type
ORDER BY
hour,
trips DESC;Здесь ClickHouse читает несколько columns, вычисляет час для каждой строки, группирует данные по двум dimensions и выполняет сразу несколько агрегатных функций.
Запрос обрабатывает 2 964 606 строк — на 18 меньше общего размера таблицы, поскольку мы ограничиваем выборку календарным январем 2024 года.
После выполнения clickhouse-client выводит время запроса и скорость обработки. Эти значения будем использовать для сравнения первого и повторных запусков.
Сравниваем cold и повторный запуск
Один и тот же агрегирующий запрос запускаем несколько раз подряд без изменений.
Первый запуск можно использовать как приближение к более холодному чтению, а повторные — как показатель работы после того, как часть нужных data pages уже могла попасть в filesystem cache.
Это не строгий synthetic cold-cache benchmark: данные могли частично закешироваться еще во время импорта или предыдущих запросов. Но для прикладного теста такой подход полезнее, потому что показывает поведение ClickHouse в обычных условиях эксплуатации.
Если повторные запуски выполняются быстрее, это само по себе нормально и не означает изменение SQL или данных — меняется состояние cache.
Как корректно интерпретировать результаты

Полученные значения относятся к нашему стенду:
- 4 vCPU;
- 8 GB RAM;
- SSD/NVMe;
- ClickHouse версии, установленной на сервере;
- dataset из 2 964 624 строк.
На другом VPS те же запросы могут выполняться быстрее или медленнее. На результат влияют производительность CPU и storage, объем RAM, состояние filesystem cache, текущие background merges, параллельные queries, структура таблицы, ORDER BY и набор читаемых columns.
Поэтому benchmark лучше воспринимать не как универсальное обещание производительности ClickHouse, а как воспроизводимую точку сравнения для конкретной конфигурации.
Смотрим, сколько места занимает таблица
Исходный Parquet-файл занимает около 50 MB, но после импорта данные уже хранятся не в Parquet. ClickHouse преобразует их в собственный формат MergeTree, создает column files, marks и metadata, а затем применяет compression.
Поэтому размер yellow_tripdata_2024-01.parquet и demo.trips не обязан совпадать.
Почему исходный файл и таблица ClickHouse отличаются по размеру
На размер таблицы влияют сразу несколько факторов:
- Мы импортировали только 10 columns исходного dataset;
- Значения были приведены к выбранным типам ClickHouse;
- Каждый столбец хранится отдельно;
- ClickHouse применяет compression;
MergeTreeсоздает marks и metadata;- Таблица состоит из одного или нескольких data parts.
В нашем случае это особенно важно: исходный Parquet содержит больше полей, чем demo.trips, поэтому напрямую сравнивать размер исходного файла и таблицы ClickHouse некорректно.
Parquet уже является сжатым column-oriented форматом, а после импорта данные дополнительно преобразуются во внутреннее представление MergeTree. Чтобы оценить фактический объем таблицы, лучше смотреть данные непосредственно через системные таблицы ClickHouse.
Как посмотреть compressed и uncompressed size
Основная информация доступна в system.parts:
SELECT
sum(rows) AS rows,
formatReadableSize(sum(bytes_on_disk)) AS disk_size,
formatReadableSize(sum(data_compressed_bytes)) AS compressed_size,
formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed_size
FROM system.parts
WHERE database = 'demo'
AND table = 'trips'
AND active;В результате ClickHouse покажет:
- Количество строк;
- Фактический размер активных parts на диске;
- Объем compressed column data;
- Приблизительный объем тех же данных после decompression.
Здесь bytes_on_disk показывает реальное место, которое занимают активные parts, data_compressed_bytes — размер сжатых данных, а data_uncompressed_bytes — их объем до compression.
Эти показатели позволяют оценить физический footprint таблицы и эффективность сжатия без необходимости вручную подставлять конкретные значения в текст.
Однако на итоговый размер влияет еще и количество parts.
Как проверить количество parts
Каждая загрузка в MergeTree создает data part, а затем background merges могут объединять несколько небольших parts в более крупные.
Количество активных parts:
SELECT count() AS active_parts
FROM system.parts
WHERE database = 'demo'
AND table = 'trips'
AND active;Посмотреть их подробнее можно так:
SELECT
name,
rows,
formatReadableSize(bytes_on_disk) AS size_on_disk
FROM system.parts
WHERE database = 'demo'
AND table = 'trips'
AND active
ORDER BY rows DESC;После одной крупной вставки parts обычно немного. Совсем другая картина возникает, если приложение постоянно отправляет тысячи крошечных INSERT: количество parts растет, а ClickHouse приходится чаще выполнять background merges.
После проверки структуры таблицы можно посчитать коэффициент compression.
Как оценить коэффициент сжатия
Для этого сравним uncompressed и compressed объем:
SELECT
formatReadableSize(sum(data_compressed_bytes)) AS compressed,
formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed,
round(
sum(data_uncompressed_bytes) /
sum(data_compressed_bytes),
2
) AS compression_ratio
FROM system.parts
WHERE database = 'demo'
AND table = 'trips'
AND active;Например, коэффициент 4.00 означал бы, что логический объем uncompressed column data примерно в четыре раза больше compressed representation.
Чем выше ratio, тем эффективнее данные сжимаются, но сравнивать разные tables только по этому показателю не стоит. Результат зависит от типов столбцов, повторяемости значений, порядка данных и codecs.
Фактические значения compressed, uncompressed и compression_ratio можно получить напрямую из system.parts приведенным выше запросом. Этого достаточно, чтобы оценить эффективность сжатия без дополнительного дублирования тех же показателей в отдельной таблице.
Теперь мы знаем не только скорость обработки dataset, но и его физический footprint внутри ClickHouse. Дальше можно перейти к проверке доступа: отдельно протестировать локальное и удаленное подключение, правильные credentials и отказ при недостаточных permissions.
Проверяем доступ к ClickHouse
Проверяем локальное подключение
На самом ClickHouse VPS выполняем:
clickhouse-client \
--user app_user \
--passwordПосле ввода корректного password проверяем:
SELECT currentUser();Ожидаем:
app_userЗатем выполняем простой запрос:
SELECT count()
FROM demo.trips;Если соединение устанавливается, пользователь определяется правильно, а таблица читается, локальный доступ работает.
Теперь можно проверить ту же схему уже с другого host.
Проверяем удаленное подключение
С доверенного клиента подключаемся к ClickHouse по адресу сервера:
clickhouse-client \
--host 203.0.113.10 \
--port 9000 \
--user app_user \
--passwordПосле подключения повторяем:
SELECT currentUser();и:
SELECT count()
FROM demo.trips;Если private network настроена, вместо публичного documentation IP используется private address ClickHouse VPS.
При успешном подключении одновременно подтверждаются несколько вещей:
- ClickHouse слушает нужный interface;
- port 9000 доступен;
- firewall пропускает доверенный source;
- пользователь существует;
- credentials подходят.
Если соединение не устанавливается вообще, проблема обычно находится ниже уровня SQL.
Проверяем пользователя и пароль
Теперь специально вводим неправильный password:
clickhouse-client \
--host 203.0.113.10 \
--port 9000 \
--user app_user \
--passwordСетевое соединение до сервера при этом может быть установлено, но ClickHouse должен отклонить authentication.
После этого повторяем подключение с корректным password и выполняем:
SELECT currentUser();Так можно отделить доступность самого порта от успешной authentication.
Следующий сценарий немного другой: пользователь входит успешно, но пытается выполнить запрещенную операцию.
Проверяем недостаточные права
Ранее app_user получил права только в пределах demo.*.
Попробуем выполнить действие за пределами нужной database, например создать объект в другой database или выполнить административную операцию, на которую права не выдавались.
Например:
CREATE DATABASE forbidden_test;ClickHouse должен вернуть ошибку недостаточных privileges.
При этом обычный запрос к разрешенной таблице:
SELECT count()
FROM demo.trips;по-прежнему должен выполняться успешно.
Получается важное различие:
- authentication → кто подключился с корректным паролем
- authorization → что этому пользователю разрешено
Если login успешен, но отдельный SQL завершается ошибкой permissions, проблема уже не в password и не в firewall.
Как отличить сетевую ошибку от ошибки authentication
На практике симптомы довольно разные.
Если client получает:
Connection refusedсервер либо не слушает этот address/port, либо соединение отклоняется на сетевом уровне.
Если соединение зависает и заканчивается timeout, стоит проверить routing, firewall и доступность host.
Если ClickHouse отвечает ошибкой authentication, значит до сервера client уже дошел, но credentials не приняты.
Если же пользователь вошел и получил ошибку privileges, значит сеть и authentication работают, а проблема только в правах.
Свести это можно так:
| Сценарий | Результат | Что означает |
Локальный clickhouse-client подключается | SQL выполняется | Server и local access работают |
| Удаленный client подключается | SQL выполняется | listen_host, routing и firewall настроены правильно |
| Connection refused | Соединение отклонено | ClickHouse не слушает port или service недоступен |
| Timeout | Ответа нет | Возможна проблема firewall, routing или address |
| Неверный password | Authentication error | Сеть работает, credentials неверны |
| SQL запрещен | Privilege error | Login успешен, но permissions недостаточно |
Доступ проверен на всех основных уровнях. Теперь можно перейти к резервному копированию и убедиться, что database можно восстановить после ошибки или удаления данных.
Создаем резервную копию ClickHouse
Backup имеет смысл только тогда, когда он охватывает нужные данные и хранится отдельно от основной таблицы. Встроенный механизм ClickHouse позволяет создавать резервные копии database или отдельных tables и затем использовать их через RESTORE.
Для тестового стенда создадим backup всей demo database.
Что именно нужно резервировать
Если приложение использует demo.trips, минимальный backup должен содержать саму table и ее data.
Можно резервировать:
- Отдельную table;
- Несколько tables;
- Всю database.
Для нашего сценария удобнее сохранить:

одной операцией.
При этом backup данных не заменяет отдельное сохранение server configuration и access-control settings, если они тоже критичны для recovery. Здесь проверяем именно восстановление dataset.
Следующий вопрос — почему не стоит просто копировать /var/lib/clickhouse.
Чем встроенный backup отличается от копирования каталога данных
Простое копирование:
/var/lib/clickhouse/работающего сервера — плохой универсальный способ backup.
Во время копирования ClickHouse продолжает менять parts, выполнять merges и обновлять metadata. В результате набор файлов может оказаться несогласованным.
Встроенный BACKUP работает на уровне самой СУБД и понимает структуру tables и metadata.
То есть:
- cp
/var/lib/clickhouse→ копирование внутренних файлов BACKUPTABLE / DATABASE → логический backup средствами ClickHouse
Для обычной эксплуатации второй вариант заметно безопаснее и удобнее для последующего RESTORE.
Теперь определим, где его хранить.
Куда сохранять backup
ClickHouse поддерживает backup destinations, определенные в конфигурации.
Для локального теста можно использовать отдельный disk, например backups, направленный в каталог:
/var/lib/clickhouse/backups/С точки зрения production локальный каталог не должен быть единственной копией. Если потеряется сам VPS или disk, backup исчезнет вместе с database.
Поэтому нормальная схема выглядит так:

Внешним местом может быть Object Storage, отдельный backup server или другой storage вне основного VPS.
Для практической проверки сначала создадим локальный backup.
Практика: создаем backup тестовой database
Сначала создаем конфигурацию backup disk.
Открываем:
sudo nano /etc/clickhouse-server/config.d/backup_disk.xmlДобавляем:
<clickhouse>
<storage_configuration>
<disks>
<backups>
<type>local</type>
<path>/var/lib/clickhouse/backups/</path>
</backups>
</disks>
</storage_configuration>
<backups>
<allowed_disk>backups</allowed_disk>
</backups>
</clickhouse>Создаем каталог:
sudo mkdir -p /var/lib/clickhouse/backupsПередаем его ClickHouse:
sudo chown clickhouse:clickhouse /var/lib/clickhouse/backupsПерезапускаем server:
sudo systemctl restart clickhouse-serverПроверяем:
sudo systemctl is-active clickhouse-serverТеперь создаем backup:
BACKUP DATABASE demo
TO Disk('backups', 'demo-backup');При успешном завершении ClickHouse подтвердит создание backup.
Проверяем, что backup создан

Сначала проверим его средствами ClickHouse:
SELECT
id,
name,
status,
error
FROM system.backups
ORDER BY start_time DESC
LIMIT 5;Для завершенной операции статус должен показывать успешное состояние.
Дополнительно можно проверить каталог на уровне системы:
sudo find /var/lib/clickhouse/backups -maxdepth 2 -type f | headИ размер:
sudo du -sh /var/lib/clickhouse/backups/demo-backupЕсли backup виден в system.backups, а файлы присутствуют в configured storage, операция завершилась корректно.
Проверяем восстановление из backup
Сам факт успешного BACKUP еще не доказывает, что резервная копия пригодна для восстановления. Ошибка в storage configuration, permissions или составе backup может обнаружиться только тогда, когда понадобится RESTORE.
Поэтому безопаснее проверить резервную копию сразу, не трогая рабочую demo: восстановим ее под другим именем и сравним данные с исходной database.
Почему backup нужно реально восстанавливать
Резервная копия считается полезной только тогда, когда из нее можно получить рабочие tables и данные.
Проверка должна подтвердить сразу несколько вещей:
- backup читается ClickHouse;
- metadata tables сохранена;
- данные доступны после restore;
- количество строк совпадает;
- контрольные запросы возвращают ожидаемый результат.
Для теста используем отдельную database:
demo_restoreТак исходная demo останется нетронутой, а восстановленную копию можно будет спокойно проверить и затем удалить.
Сначала зафиксируем несколько контрольных значений в исходной таблице.
Фиксируем контрольные данные
Проверяем количество строк:
SELECT count()
FROM demo.trips;Ожидаемый результат:
2964624Теперь посмотрим несколько агрегатов:
SELECT
min(pickup_datetime) AS first_trip,
max(pickup_datetime) AS last_trip,
count() AS rows
FROM demo.trips;Дополнительно можно выбрать несколько строк:
SELECT
pickup_datetime,
passenger_count,
trip_distance,
fare_amount,
total_amount
FROM demo.trips
ORDER BY pickup_datetime
LIMIT 5;Эти запросы дадут контрольную точку, с которой затем сравним восстановленную таблицу.
Теперь подготовим отдельное место для RESTORE.
Удаляем тестовую таблицу или создаем отдельную точку восстановления
Удалять исходную demo.trips ради проверки backup необязательно. Гораздо безопаснее восстановить database под другим именем:
demo→ исходная databasedemo_restore→ восстановленная копия
Перед тестом убедимся, что такой database еще нет:
DROP DATABASE IF EXISTS demo_restore;Это не затрагивает исходные данные и позволяет проверить полный сценарий recovery без лишнего риска.
После этого можно запускать восстановление.
Выполняем RESTORE
Для операций backup/restore используем административного пользователя с необходимыми правами.
Восстанавливаем сохраненную demo как demo_restore:
RESTORE DATABASE demo AS demo_restore
FROM Disk('backups', 'demo-backup');ClickHouse восстановит metadata database и данные tables из backup.
Состояние операции можно проверить через:
SELECT
id,
name,
status,
error
FROM system.backups
ORDER BY start_time DESC
LIMIT 5;После успешного завершения проверяем, появилась ли database:
SHOW DATABASES;А затем table:
SHOW TABLES FROM demo_restore;В списке должна быть:
tripsНо наличие самой table еще не гарантирует, что данные восстановились полностью. Поэтому завершим проверку контрольными запросами.
Проверяем количество строк и результаты запросов

Считаем строки:
SELECT count()
FROM demo_restore.trips;Ожидаем:
2964624Теперь повторяем агрегат:
SELECT
min(pickup_datetime) AS first_trip,
max(pickup_datetime) AS last_trip,
count() AS rows
FROM demo_restore.trips;Результаты должны совпасть с исходной demo.trips.
Можно проверить обе tables одним запросом:
SELECT
'original' AS source,
count() AS rows
FROM demo.trips
UNION ALL
SELECT
'restored' AS source,
count() AS rows
FROM demo_restore.trips;Получим одинаковое количество строк:
original 2964624
restored 2964624После проверки тестовую копию можно удалить:
DROP DATABASE demo_restore;Исходная demo при этом остается без изменений.
Теперь резервная копия проверена полноценным restore, а не только фактом успешного создания. Следующий важный сценарий — ошибки импорта: разберем, почему ClickHouse иногда отклоняет входные данные и как быстро найти причину.
Исправляем типичные ошибки импорта
Даже корректная таблица не гарантирует успешный импорт. Проблема может оказаться в формате файла, количестве столбцов, типах данных или ресурсах самого VPS.
Удобнее всего диагностировать такие ошибки последовательно: сначала проверить формат и schema источника, затем соответствие columns и только после этого переходить к памяти и состоянию сервера.
Что делать при несовпадении числа столбцов
Эта проблема особенно часто встречается при импорте CSV, TSV и других текстовых форматов.
Например, таблица ожидает четыре значения:
pickup_datetime, passenger_count, trip_distance, fare_amountа строка содержит только три или, наоборот, пять.
При прямом импорте:
INSERT INTO demo.trips
FORMAT CSV;ClickHouse должен понимать, каким columns соответствуют входные поля. Если структура не совпадает, импорт завершается ошибкой.
Перед загрузкой полезно явно перечислять целевые columns:
INSERT INTO demo.trips
(
pickup_datetime,
passenger_count,
trip_distance,
fare_amount
)
FORMAT CSV;Для Parquet ситуация удобнее: файл хранит schema, а в нашем случае мы дополнительно выполняем INSERT ... SELECT и сами сопоставляем исходные поля с целевыми:
SELECT
tpep_pickup_datetime AS pickup_datetime,
...
FROM file('yellow_tripdata_2024-01.parquet', Parquet);Если исходная schema изменилась, сначала проверяем ее через:
DESCRIBE file('yellow_tripdata_2024-01.parquet', Parquet);После структуры стоит проверить уже сами значения.
Почему возникает Cannot parse input
Ошибки parsing характерны прежде всего для текстовых форматов, когда ClickHouse не может преобразовать входное значение согласно ожидаемому типу или синтаксису.
Например, столбец ожидает число:
trip_distance Float64а во входных данных встречается:
unknownАналогичная проблема возникает с датами:
2024-01-15 13:45:00может корректно преобразоваться в DateTime, тогда как произвольная строка не сможет.
При ошибке parsing первым делом стоит проверить несколько исходных строк и убедиться, что выбран правильный input format.
Для файла средствами ClickHouse:
SELECT *
FROM file('yellow_tripdata_2024-01.parquet', Parquet)
LIMIT 10;Для текстового файла полезно дополнительно посмотреть его начало обычными системными средствами:
head -n 10 data.csvЕсли структура выглядит нормально, переходим к типам.
Что делать при неправильном типе данных
Тип источника должен либо совпадать с целевым column type, либо корректно к нему преобразовываться.
Например:
toUInt16(PULocationID)работает, пока значения помещаются в диапазон UInt16 и могут быть преобразованы в число.
Перед импортом можно проверить диапазон:
SELECT
min(PULocationID),
max(PULocationID)
FROM file('yellow_tripdata_2024-01.parquet', Parquet);Для nullable поля стоит отдельно проверить NULL:
SELECT countIf(passenger_count IS NULL)
FROM file('yellow_tripdata_2024-01.parquet', Parquet);Если источник допускает NULL, а target column — нет, нужно либо использовать Nullable(...), либо заранее определить, каким значением заменять пропуски.
Например:
coalesce(passenger_count, 0)Но такую замену стоит делать только тогда, когда 0 действительно имеет корректный смысл для dataset.
Если types выглядят правильно, следующая частая причина — неверно выбранный format.
Как диагностировать ошибку формата
Если CSV попытаться прочитать как TSV или указать Parquet для другого типа файла, ClickHouse не сможет правильно интерпретировать содержимое.
Поэтому формат лучше проверять до импорта.
Для нашего файла:
DESCRIBE file('yellow_tripdata_2024-01.parquet', Parquet);Если ClickHouse успешно показывает schema, значит Parquet распознается корректно.
Можно выполнить и небольшую выборку:
SELECT *
FROM file('yellow_tripdata_2024-01.parquet', Parquet)
LIMIT 1;Если ошибка возникает уже здесь, проблема находится в исходном файле, формате или доступе к нему, а не в demo.trips.
Для CSV также стоит проверить:
- delimiter;
- наличие header;
- quoting;
- encoding;
- количество полей;
- представление NULL.
После ошибок структуры и формата остается более системная проблема — нехватка ресурсов.
Что делать при нехватке памяти во время импорта
Большая загрузка может потреблять RAM на parsing, decompression, преобразование типов, sorting и формирование MergeTree parts.
Проверить память VPS:
free -hТекущие ClickHouse queries:
SELECT
query_id,
elapsed,
read_rows,
memory_usage,
query
FROM system.processes
ORDER BY memory_usage DESC;Если импорт стабильно упирается в RAM, есть несколько вариантов:
- Загружать dataset меньшими batches;
- Уменьшить параллелизм;
- Проверить memory limits ClickHouse;
- Убрать лишние преобразования;
- Увеличить RAM VPS.
При этом не стоит лечить постоянный дефицит памяти активным swap. Для аналитической database это может сильно ухудшить latency.
Если же ресурсов хватает, но соединение оборвалось, нужно понять, успела ли часть данных попасть в таблицу.
Что делать, если импорт оборвался
После неудачного INSERT первым делом проверяем:
SELECT count()
FROM demo.trips;Не стоит автоматически запускать тот же импорт второй раз, не посмотрев на результат первой попытки. Иначе можно получить дубликаты.
Проверить завершенные queries можно через:
SELECT
type,
query_duration_ms,
exception,
written_rows,
query
FROM system.query_log
WHERE query LIKE 'INSERT INTO demo.trips%'
ORDER BY event_time DESC
LIMIT 10;Если нужен полностью воспроизводимый повтор тестовой загрузки, проще очистить таблицу:
TRUNCATE TABLE demo.trips;и запустить импорт заново.
Для production datasets такой подход подходит не всегда. Там лучше строить загрузку идемпотентно: разделять данные на batches или partitions и заранее понимать, какие части уже были успешно записаны.
Основные ошибки можно свести так:
| Ошибка или симптом | Частая причина | Что проверить | Решение |
| Не совпадает число columns | Schema источника отличается от target | DESCRIBE, первые строки файла | Явно сопоставить columns |
| Cannot parse input | Значение не соответствует ожидаемому формату | Исходную строку и input format | Исправить формат или преобразование |
| Ошибка преобразования типа | Значение не помещается или не конвертируется | min(), max(), NULL | Изменить column type или conversion |
| Файл не читается | Неверный format или поврежденный source | DESCRIBE file(...), LIMIT 1 | Указать правильный format |
| Memory limit exceeded | Импорт требует слишком много RAM | free -h, system.processes | Уменьшить batch/parallelism или увеличить RAM |
| Импорт оборвался | Ошибка query, network или ресурсы | count(), system.query_log | Проверить записанные данные перед повтором |
Диагностика импорта поэтому обычно начинается не с изменения server settings, а с простых вещей: schema, format, types и нескольких строк исходного файла. Если они корректны, уже имеет смысл переходить к памяти, logs и состоянию самого ClickHouse.
Диагностируем проблемы подключения
Что означает Connection refused
Ошибка вида:
Connection refusedобычно означает, что TCP-соединение до указанного address и port было отклонено.
Чаще всего причина одна из следующих:
clickhouse-serverне запущен;- ClickHouse не слушает нужный interface;
- используется неправильный port;
- сервер слушает только
127.0.0.1; - firewall или другая сетевая настройка мешает подключению.
Например, если client пытается подключиться к native port:
clickhouse-client \
--host 203.0.113.10 \
--port 9000 \
--user app_user \
--passwordа соединение не устанавливается вообще, сначала нужно проверить инфраструктуру, а не password.
Следующий шаг — убедиться, что сам ClickHouse работает.
Как проверить clickhouse-server
На сервере выполняем:
sudo systemctl status clickhouse-serverБыстрая проверка:
sudo systemctl is-active clickhouse-serverЕсли сервис работает, получим:
activeЕсли он остановлен:
sudo systemctl start clickhouse-serverПосле этого полезно сразу проверить локальный SQL:
clickhouse-client --query "SELECT 1"Если локальный запрос проходит, значит server process работает, и проблему нужно искать уже в сетевой части.
Для начала посмотрим, какие ports ClickHouse реально слушает.
Как проверить порты
На ClickHouse VPS выполняем:
sudo ss -lntp | grep clickhouseИли точечно:
sudo ss -lntp | grep -E ':9000|:8123'Для native protocol нас интересует:
9000/TCPДля HTTP interface:
8123/TCPВажно смотреть не только номер port, но и address.
Например:
127.0.0.1:9000означает, что native interface доступен только локально.
Если удаленное подключение действительно требуется, ClickHouse должен слушать нужный private или другой разрешенный interface.
Тогда проверяем listen_host.
Как проверить listen_host
Дополнительные server settings мы храним в:
/etc/clickhouse-server/config.d/Например:
sudo cat /etc/clickhouse-server/config.d/listen.xmlДля локального доступа там может быть:
<clickhouse>
<listen_host>127.0.0.1</listen_host>
</clickhouse>Если нужен private network:
<clickhouse>
<listen_host>127.0.0.1</listen_host>
<listen_host>10.0.0.10</listen_host>
</clickhouse>После изменения server configuration ClickHouse нужно перезапустить:
sudo systemctl restart clickhouse-serverИ снова проверить:
sudo ss -lntp | grep -E ':9000|:8123'Если нужный address появился, переходим к firewall.
Как проверить firewall
Для UFW:
sudo ufw status numberedЕсли разрешен только один доверенный client, правило для native protocol может выглядеть так: 203.0.113.25 → 9000/tcp
Добавляется оно командой:
sudo ufw allow from 203.0.113.25 to any port 9000 proto tcpС самого клиента можно проверить TCP:
nc -vz 203.0.113.10 9000Если port доступен, nc сообщает об успешном соединении.
Если соединение зависает до timeout, стоит проверить:
- source IP;
- routing;
- UFW;
- firewall облачного провайдера;
- правильность address сервера.
Если TCP работает, а clickhouse-client возвращает authentication error, сеть уже не является основной проблемой.
Последний источник информации — logs.
Где смотреть логи ClickHouse
Последние сообщения systemd:
sudo journalctl \
-u clickhouse-server \
-n 100 \
--no-pagerЛоги текущей загрузки:
sudo journalctl \
-u clickhouse-server \
-b \
--no-pagerТакже при стандартной пакетной конфигурации стоит проверить:
/var/log/clickhouse-server/Например:
sudo tail -n 100 /var/log/clickhouse-server/clickhouse-server.logИ ошибки:
sudo tail -n 100 /var/log/clickhouse-server/clickhouse-server.err.logТаблица помогает быстро определить направление диагностики:
| Симптом | Что проверить | Возможная причина |
| Connection refused | systemctl, ss | Service не работает или port не слушается |
| Timeout | nc, UFW, routing | Firewall или network path |
| Локально работает, удаленно нет | listen_host, ss, UFW | ClickHouse слушает только localhost или блокируется сеть |
| Authentication error | User и password | Неверные credentials |
| Privilege error | SHOW GRANTS | Недостаточно permissions |
| Service не стартует | journalctl, server logs | Ошибка configuration или runtime |
Если проверять подключение именно в таком порядке, довольно быстро становится понятно, на каком уровне возникла проблема.
Теперь осталось убедиться, что все настройки переживают полный reboot VPS.
Проверяем ClickHouse после перезагрузки
До этого мы отдельно проверяли installation, access, dataset и backup. Финальная проверка — перезагрузить весь VPS и убедиться, что ClickHouse возвращается в рабочее состояние автоматически.
Здесь важны не только systemd service, но и пользователь, таблица, данные и обычный аналитический запрос.
Что должно запуститься автоматически
После установки ClickHouse мы включили autostart:
sudo systemctl enable clickhouse-serverПоэтому после:
sudo rebootsystemd должен автоматически запустить clickhouse-server.
Вместе с ним должны примениться:
- server configuration;
listen_host;- users и permissions;
- storage configuration;
- доступ к существующим tables;
- backup disk settings.
После повторного подключения по SSH начинаем с service.
Проверяем service
Выполняем:
sudo systemctl is-active clickhouse-serverОжидаемый результат:
activeПроверяем autostart:
sudo systemctl is-enabled clickhouse-serverОжидаем:
enabledЕсли service не поднялся:
sudo journalctl \
-u clickhouse-server \
-b \
--no-pagerТак сразу видно ошибки именно текущего boot.
Если server запущен, проверяем пользователя.
Проверяем пользователя
Подключаемся:
clickhouse-client \
--user app_user \
--passwordПосле входа:
SELECT currentUser();Получаем:
app_userЭто подтверждает, что access configuration сохранилась после reboot.
Затем проверяем разрешенную database:
SHOW TABLES FROM demo;В списке должна остаться:
tripsТеперь переходим непосредственно к данным.
Проверяем таблицу и данные
Считаем строки:
SELECT count()
FROM demo.trips;Ожидаем:
2964624Можно дополнительно проверить границы данных:
SELECT
min(pickup_datetime) AS min_pickup,
max(pickup_datetime) AS max_pickup
FROM demo.trips;Если таблица доступна и количество строк не изменилось, данные успешно пережили reboot VPS.
Осталось выполнить обычный workload.
Повторяем контрольный запрос
Используем один из уже знакомых запросов:
SELECT
pickup_location_id,
count() AS trips,
round(avg(total_amount), 2) AS avg_total
FROM demo.trips
WHERE pickup_datetime >= '2024-01-01 00:00:00'
AND pickup_datetime < '2024-02-01 00:00:00'
GROUP BY pickup_location_id
ORDER BY trips DESC
LIMIT 10;Если ClickHouse возвращает результат, финальная цепочка проверки выглядит так:

Для быстрой проверки основные шаги, как обычно, можно свести в таблицу:
| Что проверяем | Команда | Ожидаемый результат |
| Service | systemctl is-active clickhouse-server | Service активен |
| Access | SELECT currentUser() | app_user |
| Table | SHOW TABLES FROM demo | trips доступна |
| Rows | SELECT count() FROM demo.trips | 2964624 |
| Query | Контрольный агрегирующий SELECT | Запрос выполняется успешно |
Если все пять проверок проходят после полного reboot, ClickHouse возвращается в рабочее состояние автоматически: service запускается, access configuration сохраняется, таблица остается доступной, а данные и запросы работают без повторной настройки.
Как безопасно использовать ClickHouse в production
Перед production остается закрепить несколько эксплуатационных правил.
Они не требуют сложного кластера, но сильно снижают риск того, что проблема с сетью, диском или тяжелым запросом внезапно положит весь сервер.
Не открывать порты всему интернету
Native port 9000 и HTTP port 8123 не стоит оставлять доступными для всех адресов.
Лучший вариант — использовать: private network + firewall + ограниченный listen_host.
Если удаленное подключение действительно нужно, разрешайте только конкретные source IP.
Например:
sudo ufw allow from 203.0.113.25 to any port 9000 proto tcpТак ClickHouse остается доступным нужному клиенту, но не превращается в публично торчащую database.
Разделять пользователей и права
Для приложения, ETL и административных задач лучше использовать разные accounts.
Например:
- admin_user → управление users и schema
- etl_user →
INSERT/SELECT app_user→ только нужныеSELECT
Права можно проверять через:
SHOW GRANTS FOR app_user;Так проще контролировать доступ и уменьшить последствия ошибки или компрометации одного account.
Следить за свободным диском
Для ClickHouse disk — критичный ресурс.
Он используется не только для самих данных, но и для:
- merges;
- temporary files;
- backup;
- logs;
- новых parts.
Поэтому проверять только размер таблицы недостаточно.
На уровне системы:
df -hРазмер ClickHouse:
sudo du -sh /var/lib/clickhouseА размер отдельных tables можно смотреть через system.parts.
Если свободное место подходит к концу, проблемы могут проявиться сразу в нескольких местах: INSERT, merge, backup и даже обычные background operations.
Оставлять запас RAM для тяжелых запросов
ClickHouse активно использует RAM при:
GROUP BY;ORDER BY;JOIN;- large aggregations;
- parsing и decompression;
- создании intermediate states.
Поэтому серверу нужен запас памяти сверх обычного baseline.
Проверить системную RAM:
free -hАктивные queries:
SELECT
query_id,
elapsed,
memory_usage,
query
FROM system.processes
ORDER BY memory_usage DESC;Если один запрос стабильно съедает почти всю память VPS, лучше ограничить workload или увеличить RAM, а не надеяться на swap.
Контролировать рост parts
MergeTree хранит данные в parts, которые затем объединяются background merges.
Если приложение отправляет слишком много мелких INSERT, количество parts может быстро расти.
Проверить их можно так:
SELECT
database,
table,
count() AS parts
FROM system.parts
WHERE active
GROUP BY
database,
table
ORDER BY parts DESC;Если parts становятся слишком многочисленными, стоит пересмотреть стратегию загрузки и отправлять данные более крупными batches.
Для ClickHouse обычно лучше: реже + крупнее INSERT, чем тысячи микроскопических вставок.
Хранить backup вне ClickHouse VPS
Backup на том же диске полезен для быстрой проверки восстановления, но не защищает от потери самого VPS.
Production-схема должна выглядеть примерно так:

Это может быть Object Storage, другой VPS или отдельное backup storage.
Если backup лежит только рядом с /var/lib/clickhouse, отказ storage уничтожит и данные, и резервную копию одновременно.
Проверять RESTORE
Создать backup недостаточно.
Нужно периодически убеждаться, что он реально восстанавливается.
Мы уже использовали безопасную схему:

Такой тест полезно повторять регулярно, особенно после изменений storage configuration или обновления ClickHouse.
Лучше обнаружить проблему с backup заранее, чем в момент аварии.
Контролировать query log и ошибки
ClickHouse хранит много полезной информации в системных таблицах.
Например, историю запросов можно смотреть через:
SELECT
event_time,
query_duration_ms,
read_rows,
written_rows,
memory_usage,
exception,
query
FROM system.query_log
ORDER BY event_time DESC
LIMIT 20;Для текущих запросов:
SELECT *
FROM system.processes;На уровне системы также полезны:
sudo journalctl -u clickhouse-server -n 100 --no-pagerи:
sudo tail -n 100 /var/log/clickhouse-server/clickhouse-server.err.logТак можно быстрее заметить memory errors, неудачные queries, проблемы с disk и ошибки configuration.
Обновлять ClickHouse последовательно
Обновление ClickHouse лучше не выполнять вслепую.
Перед upgrade стоит проверить:
- текущую версию;
- changelog целевой версии;
- compatibility;
- backup;
- свободное место;
- состояние tables;
- возможность rollback.
Версию сервера:
SELECT version();После обновления повторяем базовые проверки:
sudo systemctl status clickhouse-server
SELECT 1;
SHOW DATABASES;
SELECT count()
FROM demo.trips;И отдельно проверяем пользователя, backup configuration и контрольный аналитический запрос.
Что проверить перед production
Перед запуском реальной нагрузки удобно пройти короткий checklist.
Сеть и доступ
- ports не открыты всему интернету;
listen_hostнастроен осознанно;- firewall разрешает только нужные source IP;
- у приложений отдельные users;
- permissions минимально необходимые.
Ресурсы
- disk имеет достаточный запас;
- RAM не занята под завязку;
- тяжелые queries не выбивают сервер по памяти;
- количество
activeparts остается разумным; - нет постоянного pressure на storage.
Данные и восстановление
- backup создается без ошибок;
- backup хранится вне основного VPS;
RESTOREреально проверен;- контрольный dataset после restore читается;
- reboot не ломает доступ к tables.
Эксплуатация
clickhouse-serverзапускается автоматически;- query log доступен;
- server logs проверяются;
- upgrades выполняются с backup и post-check;
- backup создается регулярно в соответствии с политикой восстановления;
- benchmark оценивается вместе с конфигурацией VPS, а не сам по себе.
Если эти пункты закрыты, standalone ClickHouse уже можно использовать как предсказуемый production-компонент для аналитических workloads без необходимости сразу переходить к кластерной архитектуре.
Заключение

ClickHouse на одном облачном сервере хорошо подходит для небольших аналитических workloads, логов, событий и других сценариев, где важны быстрые агрегаты по большим наборам данных. В нашем случае standalone-сервера оказалось достаточно, чтобы загрузить несколько миллионов строк NYC Taxi, выполнить аналитические запросы, проверить доступ и посмотреть, как MergeTree хранит данные.
Для production важно не ограничиваться установкой: доступ лучше держать за firewall, пользователям выдавать минимальные права, следить за RAM, disk и количеством parts, а backup хранить вне основного VPS и периодически проверять через RESTORE.
Спасибо за внимание!
FAQ
Подходит ли ClickHouse вместо PostgreSQL или MySQL?
Не всегда. ClickHouse в первую очередь рассчитан на аналитические workloads (OLAP): большие выборки, агрегации, фильтрацию и обработку миллионов строк. Для классического OLTP с частыми точечными UPDATE, транзакциями и большим количеством небольших операций PostgreSQL или MySQL обычно подходят лучше.
Обязательно ли использовать MergeTree?
Для большинства обычных таблиц ClickHouse семейство MergeTree — основной вариант. Оно поддерживает хранение данных в parts, background merges и эффективное чтение по sorting key. Другие engines нужны уже для более специализированных сценариев.
Почему после импорта Parquet таблица занимает другой объем?
Parquet и MergeTree используют разные форматы хранения и compression. Кроме того, в нашем случае в ClickHouse импортируется только часть исходных columns. Поэтому размеры исходного файла и таблицы нельзя напрямую сравнивать как два идентичных представления одного dataset.
Что произойдет, если часто выполнять маленькие INSERT?
Каждая вставка может создавать новый data part. При слишком большом количестве мелких вставок ClickHouse приходится чаще выполнять background merges, а число parts начинает расти. Поэтому данные обычно выгоднее загружать более крупными batches.
Можно ли подключаться к ClickHouse через HTTP?
Да. Помимо native protocol на 9000, ClickHouse поддерживает HTTP interface, который обычно работает через 8123. Его используют приложения, scripts и integrations, которым удобнее отправлять запросы по HTTP.
Нужно ли хранить весь dataset в RAM?
Нет. Основные данные ClickHouse хранятся на disk. RAM используется для выполнения запросов, aggregation states, sorting, joins, buffers и cache. Поэтому большой dataset не обязан полностью помещаться в память, но тяжелые запросы все равно требуют достаточного запаса RAM.
Почему повторный запрос может выполняться быстрее первого?
После первого чтения часть data pages может остаться в filesystem cache, поэтому повторный запрос иногда получает нужные данные быстрее. На результат также влияют background merges, параллельная нагрузка и состояние самого VPS, поэтому единичный замер не стоит воспринимать как универсальную характеристику производительности.
Достаточно ли встроенного BACKUP, если он хранится на том же VPS?
Для проверки восстановления — да, но для production этого недостаточно. Отказ или потеря disk может уничтожить одновременно и основную database, и локальный backup. Поэтому резервные копии лучше дополнительно переносить во внешнее Object Storage или на отдельный backup server.


