Перевод статьи подготовлен специально для студентов курса «Data Engineer».

ClickHouse — это колоночная база данных с открытым исходным кодом. Это великолепная среда, где сотни аналитиков могут быстро запрашивать развернутые данные, даже когда вводятся десятки миллиардов новых записей в день. Расходы на инфраструктуру для поддержки такой системы могут достигать 100 тыс. долларов США в год, и потенциально вдвое меньше, в зависимости от использования. В какой-то момент инсталяция ClickHouse от Яндекс Метрики содержала 10 триллионов записей. Помимо Яндекса, ClickHouse также снискала успех у Bloomberg и Cloudflare.
Два года назад я провел сравнительный анализ баз данных с использованием одной машины, и она стала самым быстрым бесплатным программным обеспечением для баз данных, которое я когда-либо видел. С тех пор разработчики не переставали добавлять фичи, включая поддержку Kafka, HDFS и ZStandard сжатия. В прошлом году они добавили поддержку каскадирования методов сжатия, и дельта-от-дельты кодирование стало возможным. При сжатии данных временных рядов gauge-значения могут хорошо сжиматься с помощью дельта-кодирования, но для счетчиков (counter) будет лучше использовать дельта-от-дельты-кодирование. Хорошее сжатие стало ключом к производительности ClickHouse.
ClickHouse состоит из 170 тысяч строк C++ кода, за исключением сторонних библиотек, и является одной из наименьших кодовых баз для распределенных баз данных. Для сравнения, SQLite не поддерживает распределение и состоит из 235 тысяч строк кода на языке С. На момент написания этой статьи свой вклад в ClickHouse внесли 207 инженеров, и интенсивность коммитов в последнее время увеличивается.
В марте 2017 года ClickHouse начал вести журнал изменений в качестве простого способа отслеживать разработку. Они также разбили монолитный файл документации на иерархию файлов на основе Markdown. Проблемы и фичи отслеживаются через GitHub, и в целом это программное обеспечение стало намного более доступным в последние несколько лет.
В этой статье я собираюсь взглянуть на производительность кластера ClickHouse на AWS EC2 с использованием 36-ядерных процессоров и NVMe-накопителя.
Я буду использовать три экземпляра c5d.9xlarge EC2 для этого поста. Каждый из них содержит 36 виртуальных ЦП, 72 ГБ ОЗУ, 900 ГБ накопителя NVMe SSD и поддерживает 10-гигабитную сеть. Они стоят $1,962/час каждый в регионе eu-west-1 при запуске по требованию. Я буду использовать Ubuntu Server 16.04 LTS в качестве операционной системы.
Фаервол настроен так, что каждая машина может связываться друг с другом без ограничений, и только мой IPv4-адрес занесен в белый список SSH в кластере.
Для работы ClickHouse я создам в NVMe-накопителе файловую систему в формате EXT4 на каждом из серверов.
После того как все настроено, вы можете увидеть точку монтирования и 783 ГБ пространства, доступного в каждой из систем.
Набор данных, который я буду использовать в этом тесте, представляет собой дамп данных, который я сформировал из 1.1 миллиарда поездок на такси, произведенных в Нью-Йорке за шесть лет. В блоге Миллиард поездок на такси в Redshift подробно рассказывается о том, как я собрал этот набор данных. Они хранятся в AWS S3, поэтому я настрою интерфейс командной строки AWS с помощью моего доступа и секретных ключей.
Я установлю ограничение количества одновременных запросов клиента на 100, чтобы файлы загружались быстрее, чем при стандартных настройках.
Я скачаю набор данных поездок на такси с AWS S3 и сохраню его на диске NVMe на первом сервере. Этот набор данных составляет ~ 104 ГБ в GZIP-сжатом CSV-формате.
Я установлю дистрибутив OpenJDK для Java 8, так как он необходим для запуска Apache ZooKeeper, необходимого для распределенной установки ClickHouse на всех трех машинах.
Затем я устанавливаю переменную среды
Затем я буду использовать систему управления пакетами в Ubuntu для установки ClickHouse 18.16.1, glances и ZooKeeper на все три машины.
Я создам каталог для ClickHouse, а также совершу некоторые переопределения конфигурации на всех трех серверах.
Это переопределения конфигурации, которые я буду использовать.
Затем я запущу ZooKeeper и сервер ClickHouse на всех трех машинах.
На первом сервере я создам таблицу поездок (
Затем я распаковываю и загружаю каждый из CSV-файлов в таблицу поездок (
Скорость импорта составляла 155 МБ несжатого CSV-контента в секунду. Я подозреваю, что это было связано с узким местом в GZIP-декомпрессии. Возможно, быстрее было распаковать все файлы gzip параллельно, используя xargs, а затем загрузить распакованные данные. Ниже приведено описание того, что сообщалось в процессе импорта CSV.
Я освобожу место на NVMe-приводе, удалив исходные CSV-файлы, прежде чем продолжить.
Движок Log ClickHouse будет хранить данные в строко-ориентированном формате. Чтобы быстрее запрашивать данные, я конвертирую их в колоночный формат с помощью движка MergeTree.
Следующее выполнено за 34 минуты и 50 секунд. После этой операции размер каталога данных составил 237 ГБ.
Вот как выглядел glance-вывод во время операции:
В последнем тесте несколько столбцов были преобразованы и пересчитаны. Я обнаружил, что некоторые из этих функций больше не работают должным образом в этом наборе данных. Для решения этой проблемы я удалил неподходящие функции и загрузил данные без преобразования в более детализированные типы.
Я буду распределять данные по всем трем узлам кластера. Для начала ниже я создам таблицу на всех трех машинах.
Затем я позабочусь о том, чтобы первый сервер мог видеть все три узла в кластере.
Затем я определю новую таблицу на первом сервере, которая основана на схеме
Затем я скопирую данные из таблицы на основе MergeTree на все три сервера. Следующее выполнено за 34 минуты и 44 секунды.
После приведенной выше операции я дал ClickHouse 15 минут, чтобы отойти от отметки максимального уровня хранилища. Каталоги данных в конечном итоге составляли 264 ГБ, 34 ГБ и 33 ГБ соответственно на каждом из трех серверов.
То, что я увидел дальше, было самым быстрым временем, которое я видел при многократном выполнении каждого запроса в таблице
Следующее выполнено за 2.449 секунды.
Следующее выполнено за 0.691 секунды.
Следующее выполнено за 0.,582 секунды.
Следующее выполнено за 0.983 секунды.
Для сравнения я выполнил те же запросы в таблице на основе MergeTree, которая находится исключительно на первом сервере.
То, что я увидел дальше, было самым быстрым временем, которое я видел при многократном выполнении каждого запроса в таблице
Следующее выполнено за 0.241 секунды.
Следующее выполнено за 0.826 секунды.
Следующее выполнено за 1.209 секунды.
Следующее выполнено за 1.781 секунды.
Это первый раз, когда бесплатная база данных на базе процессора смогла превзойти базу данных на основе GPU в моих тестах. Та база данных на основе GPU с тех пор подверглась двум ревизиям, но, тем не менее, производительность, которую ClickHouse показал на одном узле, очень впечатляет.
При этом при выполнении Query 1 на распределенном движке накладные расходы оказываются на порядок выше. Я надеюсь, что что-то пропустил в своем исследовании для этого поста, потому что было бы хорошо увидеть, как время запросов снижается, когда я добавляю больше узлов в кластер. Однако это замечательно, что при выполнении других запросов производительность выросла примерно в 2 раза.
Было бы неплохо, если бы ClickHouse развивался в направлении того, чтобы можно было отделить хранилище и вычисления, чтобы они могли масштабироваться независимо. Поддержка HDFS, которая была добавлена в прошлом году, может стать шагом к этому. Что касается вычислений, если один запрос может быть ускорен при добавлении большего количества узлов в кластер, то будущее этого программного обеспечения будет очень безоблачным.
Спасибо, что нашли время, чтобы прочитать этот пост. Я предлагаю консалтинг, архитектуру и услуги по практическому развитию для клиентов в Северной Америке и Европе. Если вы хотите обсудить, как мои предложения могут помочь вашему бизнесу, свяжитесь со мной через LinkedIn.

ClickHouse — это колоночная база данных с открытым исходным кодом. Это великолепная среда, где сотни аналитиков могут быстро запрашивать развернутые данные, даже когда вводятся десятки миллиардов новых записей в день. Расходы на инфраструктуру для поддержки такой системы могут достигать 100 тыс. долларов США в год, и потенциально вдвое меньше, в зависимости от использования. В какой-то момент инсталяция ClickHouse от Яндекс Метрики содержала 10 триллионов записей. Помимо Яндекса, ClickHouse также снискала успех у Bloomberg и Cloudflare.
Два года назад я провел сравнительный анализ баз данных с использованием одной машины, и она стала самым быстрым бесплатным программным обеспечением для баз данных, которое я когда-либо видел. С тех пор разработчики не переставали добавлять фичи, включая поддержку Kafka, HDFS и ZStandard сжатия. В прошлом году они добавили поддержку каскадирования методов сжатия, и дельта-от-дельты кодирование стало возможным. При сжатии данных временных рядов gauge-значения могут хорошо сжиматься с помощью дельта-кодирования, но для счетчиков (counter) будет лучше использовать дельта-от-дельты-кодирование. Хорошее сжатие стало ключом к производительности ClickHouse.
ClickHouse состоит из 170 тысяч строк C++ кода, за исключением сторонних библиотек, и является одной из наименьших кодовых баз для распределенных баз данных. Для сравнения, SQLite не поддерживает распределение и состоит из 235 тысяч строк кода на языке С. На момент написания этой статьи свой вклад в ClickHouse внесли 207 инженеров, и интенсивность коммитов в последнее время увеличивается.
В марте 2017 года ClickHouse начал вести журнал изменений в качестве простого способа отслеживать разработку. Они также разбили монолитный файл документации на иерархию файлов на основе Markdown. Проблемы и фичи отслеживаются через GitHub, и в целом это программное обеспечение стало намного более доступным в последние несколько лет.
В этой статье я собираюсь взглянуть на производительность кластера ClickHouse на AWS EC2 с использованием 36-ядерных процессоров и NVMe-накопителя.
АПДЕЙТ: Через неделю после первоначальной публикации этого поста я повторно запустил тест с улучшенной конфигурацией и достиг гораздо лучших результатов. Этот пост был обновлен, чтобы отразить эти изменения.
Запуск кластера AWS EC2
Я буду использовать три экземпляра c5d.9xlarge EC2 для этого поста. Каждый из них содержит 36 виртуальных ЦП, 72 ГБ ОЗУ, 900 ГБ накопителя NVMe SSD и поддерживает 10-гигабитную сеть. Они стоят $1,962/час каждый в регионе eu-west-1 при запуске по требованию. Я буду использовать Ubuntu Server 16.04 LTS в качестве операционной системы.
Фаервол настроен так, что каждая машина может связываться друг с другом без ограничений, и только мой IPv4-адрес занесен в белый список SSH в кластере.
NVMe-накопитель в состоянии рабочей готовности
Для работы ClickHouse я создам в NVMe-накопителе файловую систему в формате EXT4 на каждом из серверов.
$ sudo mkfs -t ext4 /dev/nvme1n1
$ sudo mkdir /ch
$ sudo mount /dev/nvme1n1 /ch
После того как все настроено, вы можете увидеть точку монтирования и 783 ГБ пространства, доступного в каждой из систем.
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
loop0 7:0 0 87.9M 1 loop /snap/core/5742
loop1 7:1 0 16.5M 1 loop /snap/amazon-ssm-agent/784
nvme0n1 259:1 0 8G 0 disk
└─nvme0n1p1 259:2 0 8G 0 part /
nvme1n1 259:0 0 838.2G 0 disk /ch
$ df -h
Filesystem Size Used Avail Use% Mounted on
udev 35G 0 35G 0% /dev
tmpfs 6.9G 8.8M 6.9G 1% /run
/dev/nvme0n1p1 7.7G 967M 6.8G 13% /
tmpfs 35G 0 35G 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 35G 0 35G 0% /sys/fs/cgroup
/dev/loop0 88M 88M 0 100% /snap/core/5742
/dev/loop1 17M 17M 0 100% /snap/amazon-ssm-agent/784
tmpfs 6.9G 0 6.9G 0% /run/user/1000
/dev/nvme1n1 825G 73M 783G 1% /ch
Набор данных, который я буду использовать в этом тесте, представляет собой дамп данных, который я сформировал из 1.1 миллиарда поездок на такси, произведенных в Нью-Йорке за шесть лет. В блоге Миллиард поездок на такси в Redshift подробно рассказывается о том, как я собрал этот набор данных. Они хранятся в AWS S3, поэтому я настрою интерфейс командной строки AWS с помощью моего доступа и секретных ключей.
$ sudo apt update
$ sudo apt install awscli
$ aws configure
Я установлю ограничение количества одновременных запросов клиента на 100, чтобы файлы загружались быстрее, чем при стандартных настройках.
$ aws configure set \
default.s3.max_concurrent_requests \
100
Я скачаю набор данных поездок на такси с AWS S3 и сохраню его на диске NVMe на первом сервере. Этот набор данных составляет ~ 104 ГБ в GZIP-сжатом CSV-формате.
$ sudo mkdir -p /ch/csv
$ sudo chown -R ubuntu /ch/csv
$ aws s3 sync s3://<bucket>/csv /ch/csv
Установка ClickHouse
Я установлю дистрибутив OpenJDK для Java 8, так как он необходим для запуска Apache ZooKeeper, необходимого для распределенной установки ClickHouse на всех трех машинах.
$ sudo apt update
$ sudo apt install \
openjdk-8-jre \
openjdk-8-jdk-headless
Затем я устанавливаю переменную среды
JAVA_HOME
.$ sudo vi /etc/profile
export JAVA_HOME=/usr
$ source /etc/profile
Затем я буду использовать систему управления пакетами в Ubuntu для установки ClickHouse 18.16.1, glances и ZooKeeper на все три машины.
$ sudo apt-key adv \
--keyserver hkp://keyserver.ubuntu.com:80 \
--recv E0C56BD4
$ echo "deb http://repo.yandex.ru/clickhouse/deb/stable/ main/" | \
sudo tee /etc/apt/sources.list.d/clickhouse.list
$ sudo apt-get update
$ sudo apt install \
clickhouse-client \
clickhouse-server \
glances \
zookeeperd
Я создам каталог для ClickHouse, а также совершу некоторые переопределения конфигурации на всех трех серверах.
$ sudo mkdir /ch/clickhouse
$ sudo chown -R clickhouse /ch/clickhouse
$ sudo mkdir -p /etc/clickhouse-server/conf.d
$ sudo vi /etc/clickhouse-server/conf.d/taxis.conf
Это переопределения конфигурации, которые я буду использовать.
<?xml version="1.0"?>
<yandex>
<listen_host>0.0.0.0</listen_host>
<path>/ch/clickhouse/</path>
<remote_servers>
<perftest_3shards>
<shard>
<replica>
<host>172.30.2.192</host>
<port>9000</port>
</replica>
</shard>
<shard>
<replica>
<host>172.30.2.162</host>
<port>9000</port>
</replica>
</shard>
<shard>
<replica>
<host>172.30.2.36</host>
<port>9000</port>
</replica>
</shard>
</perftest_3shards>
</remote_servers>
<zookeeper-servers>
<node>
<host>172.30.2.192</host>
<port>2181</port>
</node>
<node>
<host>172.30.2.162</host>
<port>2181</port>
</node>
<node>
<host>172.30.2.36</host>
<port>2181</port>
</node>
</zookeeper-servers>
<macros>
<shard>03</shard>
<replica>01</replica>
</macros>
</yandex>
Затем я запущу ZooKeeper и сервер ClickHouse на всех трех машинах.
$ sudo /etc/init.d/zookeeper start
$ sudo service clickhouse-server start
Загрузка данных в ClickHouse
На первом сервере я создам таблицу поездок (
trips
), в которой будет храниться набор данных поездок в такси с использованием движка Log.$ clickhouse-client --host=0.0.0.0
CREATE TABLE trips (
trip_id UInt32,
vendor_id String,
pickup_datetime DateTime,
dropoff_datetime Nullable(DateTime),
store_and_fwd_flag Nullable(FixedString(1)),
rate_code_id Nullable(UInt8),
pickup_longitude Nullable(Float64),
pickup_latitude Nullable(Float64),
dropoff_longitude Nullable(Float64),
dropoff_latitude Nullable(Float64),
passenger_count Nullable(UInt8),
trip_distance Nullable(Float64),
fare_amount Nullable(Float32),
extra Nullable(Float32),
mta_tax Nullable(Float32),
tip_amount Nullable(Float32),
tolls_amount Nullable(Float32),
ehail_fee Nullable(Float32),
improvement_surcharge Nullable(Float32),
total_amount Nullable(Float32),
payment_type Nullable(String),
trip_type Nullable(UInt8),
pickup Nullable(String),
dropoff Nullable(String),
cab_type Nullable(String),
precipitation Nullable(Int8),
snow_depth Nullable(Int8),
snowfall Nullable(Int8),
max_temperature Nullable(Int8),
min_temperature Nullable(Int8),
average_wind_speed Nullable(Int8),
pickup_nyct2010_gid Nullable(Int8),
pickup_ctlabel Nullable(String),
pickup_borocode Nullable(Int8),
pickup_boroname Nullable(String),
pickup_ct2010 Nullable(String),
pickup_boroct2010 Nullable(String),
pickup_cdeligibil Nullable(FixedString(1)),
pickup_ntacode Nullable(String),
pickup_ntaname Nullable(String),
pickup_puma Nullable(String),
dropoff_nyct2010_gid Nullable(UInt8),
dropoff_ctlabel Nullable(String),
dropoff_borocode Nullable(UInt8),
dropoff_boroname Nullable(String),
dropoff_ct2010 Nullable(String),
dropoff_boroct2010 Nullable(String),
dropoff_cdeligibil Nullable(String),
dropoff_ntacode Nullable(String),
dropoff_ntaname Nullable(String),
dropoff_puma Nullable(String)
) ENGINE = Log;
Затем я распаковываю и загружаю каждый из CSV-файлов в таблицу поездок (
trips
). Следующее выполнено за 55 минут и 10 секунд. После этой операции размер каталога данных составил 134 ГБ.$ time (for FILENAME in /ch/csv/trips_x*.csv.gz; do
echo $FILENAME
gunzip -c $FILENAME | \
clickhouse-client \
--host=0.0.0.0 \
--query="INSERT INTO trips FORMAT CSV"
done)
Скорость импорта составляла 155 МБ несжатого CSV-контента в секунду. Я подозреваю, что это было связано с узким местом в GZIP-декомпрессии. Возможно, быстрее было распаковать все файлы gzip параллельно, используя xargs, а затем загрузить распакованные данные. Ниже приведено описание того, что сообщалось в процессе импорта CSV.
$ sudo glances
ip-172-30-2-200 (Ubuntu 16.04 64bit / Linux 4.4.0-1072-aws) Uptime: 0:11:42
CPU 8.2% nice: 0.0% LOAD 36-core MEM 9.8% active: 5.20G SWAP 0.0%
user: 6.0% irq: 0.0% 1 min: 2.24 total: 68.7G inactive: 61.0G total: 0
system: 0.9% iowait: 1.3% 5 min: 1.83 used: 6.71G buffers: 66.4M used: 0
idle: 91.8% steal: 0.0% 15 min: 1.01 free: 62.0G cached: 61.6G free: 0
NETWORK Rx/s Tx/s TASKS 370 (507 thr), 2 run, 368 slp, 0 oth sorted automatically by cpu_percent, flat view
ens5 136b 2Kb
lo 343Mb 343Mb CPU% MEM% VIRT RES PID USER NI S TIME+ IOR/s IOW/s Command
100.4 1.5 1.65G 1.06G 9909 ubuntu 0 S 1:01.33 0 0 clickhouse-client --host=0.0.0.0 --query=INSERT INTO trips FORMAT CSV
DISK I/O R/s W/s 85.1 0.0 4.65M 708K 9908 ubuntu 0 R 0:50.60 32M 0 gzip -d -c /ch/csv/trips_xac.csv.gz
loop0 0 0 54.9 5.1 8.14G 3.49G 8091 clickhous 0 S 1:44.23 0 45M /usr/bin/clickhouse-server --config=/etc/clickhouse-server/config.xml
loop1 0 0 4.5 0.0 0 0 319 root 0 S 0:07.50 1K 0 kworker/u72:2
nvme0n1 0 3K 2.3 0.0 91.1M 28.9M 9912 root 0 R 0:01.56 0 0 /usr/bin/python3 /usr/bin/glances
nvme0n1p1 0 3K 0.3 0.0 0 0 960 root -20 S 0:00.10 0 0 kworker/28:1H
nvme1n1 32.1M 495M 0.3 0.0 0 0 1058 root -20 S 0:00.90 0 0 kworker/23:1H
Я освобожу место на NVMe-приводе, удалив исходные CSV-файлы, прежде чем продолжить.
$ sudo rm -fr /ch/csv
Преобразование в колоночную форму
Движок Log ClickHouse будет хранить данные в строко-ориентированном формате. Чтобы быстрее запрашивать данные, я конвертирую их в колоночный формат с помощью движка MergeTree.
$ clickhouse-client --host=0.0.0.0
Следующее выполнено за 34 минуты и 50 секунд. После этой операции размер каталога данных составил 237 ГБ.
CREATE TABLE trips_mergetree
ENGINE = MergeTree(pickup_date, pickup_datetime, 8192)
AS SELECT
trip_id,
CAST(vendor_id AS Enum8('1' = 1,
'2' = 2,
'CMT' = 3,
'VTS' = 4,
'DDS' = 5,
'B02512' = 10,
'B02598' = 11,
'B02617' = 12,
'B02682' = 13,
'B02764' = 14)) AS vendor_id,
toDate(pickup_datetime) AS pickup_date,
ifNull(pickup_datetime, toDateTime(0)) AS pickup_datetime,
toDate(dropoff_datetime) AS dropoff_date,
ifNull(dropoff_datetime, toDateTime(0)) AS dropoff_datetime,
assumeNotNull(store_and_fwd_flag) AS store_and_fwd_flag,
assumeNotNull(rate_code_id) AS rate_code_id,
assumeNotNull(pickup_longitude) AS pickup_longitude,
assumeNotNull(pickup_latitude) AS pickup_latitude,
assumeNotNull(dropoff_longitude) AS dropoff_longitude,
assumeNotNull(dropoff_latitude) AS dropoff_latitude,
assumeNotNull(passenger_count) AS passenger_count,
assumeNotNull(trip_distance) AS trip_distance,
assumeNotNull(fare_amount) AS fare_amount,
assumeNotNull(extra) AS extra,
assumeNotNull(mta_tax) AS mta_tax,
assumeNotNull(tip_amount) AS tip_amount,
assumeNotNull(tolls_amount) AS tolls_amount,
assumeNotNull(ehail_fee) AS ehail_fee,
assumeNotNull(improvement_surcharge) AS improvement_surcharge,
assumeNotNull(total_amount) AS total_amount,
assumeNotNull(payment_type) AS payment_type_,
assumeNotNull(trip_type) AS trip_type,
pickup AS pickup,
pickup AS dropoff,
CAST(assumeNotNull(cab_type)
AS Enum8('yellow' = 1, 'green' = 2))
AS cab_type,
precipitation AS precipitation,
snow_depth AS snow_depth,
snowfall AS snowfall,
max_temperature AS max_temperature,
min_temperature AS min_temperature,
average_wind_speed AS average_wind_speed,
pickup_nyct2010_gid AS pickup_nyct2010_gid,
pickup_ctlabel AS pickup_ctlabel,
pickup_borocode AS pickup_borocode,
pickup_boroname AS pickup_boroname,
pickup_ct2010 AS pickup_ct2010,
pickup_boroct2010 AS pickup_boroct2010,
pickup_cdeligibil AS pickup_cdeligibil,
pickup_ntacode AS pickup_ntacode,
pickup_ntaname AS pickup_ntaname,
pickup_puma AS pickup_puma,
dropoff_nyct2010_gid AS dropoff_nyct2010_gid,
dropoff_ctlabel AS dropoff_ctlabel,
dropoff_borocode AS dropoff_borocode,
dropoff_boroname AS dropoff_boroname,
dropoff_ct2010 AS dropoff_ct2010,
dropoff_boroct2010 AS dropoff_boroct2010,
dropoff_cdeligibil AS dropoff_cdeligibil,
dropoff_ntacode AS dropoff_ntacode,
dropoff_ntaname AS dropoff_ntaname,
dropoff_puma AS dropoff_puma
FROM trips;
Вот как выглядел glance-вывод во время операции:
ip-172-30-2-200 (Ubuntu 16.04 64bit / Linux 4.4.0-1072-aws) Uptime: 1:06:09
CPU 10.3% nice: 0.0% LOAD 36-core MEM 16.1% active: 13.3G SWAP 0.0%
user: 7.9% irq: 0.0% 1 min: 1.87 total: 68.7G inactive: 52.8G total: 0
system: 1.6% iowait: 0.8% 5 min: 1.76 used: 11.1G buffers: 71.8M used: 0
idle: 89.7% steal: 0.0% 15 min: 1.95 free: 57.6G cached: 57.2G free: 0
NETWORK Rx/s Tx/s TASKS 367 (523 thr), 1 run, 366 slp, 0 oth sorted automatically by cpu_percent, flat view
ens5 1Kb 8Kb
lo 2Kb 2Kb CPU% MEM% VIRT RES PID USER NI S TIME+ IOR/s IOW/s Command
241.9 12.8 20.7G 8.78G 8091 clickhous 0 S 30:36.73 34M 125M /usr/bin/clickhouse-server --config=/etc/clickhouse-server/config.xml
DISK I/O R/s W/s 2.6 0.0 90.4M 28.3M 9948 root 0 R 1:18.53 0 0 /usr/bin/python3 /usr/bin/glances
loop0 0 0 1.3 0.0 0 0 203 root 0 S 0:09.82 0 0 kswapd0
loop1 0 0 0.3 0.1 315M 61.3M 15701 ubuntu 0 S 0:00.40 0 0 clickhouse-client --host=0.0.0.0
nvme0n1 0 3K 0.3 0.0 0 0 7 root 0 S 0:00.83 0 0 rcu_sched
nvme0n1p1 0 3K 0.0 0.0 0 0 142 root 0 S 0:00.22 0 0 migration/27
nvme1n1 25.8M 330M 0.0 0.0 59.7M 1.79M 2764 ubuntu 0 S 0:00.00 0 0 (sd-pam)
В последнем тесте несколько столбцов были преобразованы и пересчитаны. Я обнаружил, что некоторые из этих функций больше не работают должным образом в этом наборе данных. Для решения этой проблемы я удалил неподходящие функции и загрузил данные без преобразования в более детализированные типы.
Распределение данных по кластеру
Я буду распределять данные по всем трем узлам кластера. Для начала ниже я создам таблицу на всех трех машинах.
$ clickhouse-client --host=0.0.0.0
CREATE TABLE trips_mergetree_third (
trip_id UInt32,
vendor_id String,
pickup_date Date,
pickup_datetime DateTime,
dropoff_date Date,
dropoff_datetime Nullable(DateTime),
store_and_fwd_flag Nullable(FixedString(1)),
rate_code_id Nullable(UInt8),
pickup_longitude Nullable(Float64),
pickup_latitude Nullable(Float64),
dropoff_longitude Nullable(Float64),
dropoff_latitude Nullable(Float64),
passenger_count Nullable(UInt8),
trip_distance Nullable(Float64),
fare_amount Nullable(Float32),
extra Nullable(Float32),
mta_tax Nullable(Float32),
tip_amount Nullable(Float32),
tolls_amount Nullable(Float32),
ehail_fee Nullable(Float32),
improvement_surcharge Nullable(Float32),
total_amount Nullable(Float32),
payment_type Nullable(String),
trip_type Nullable(UInt8),
pickup Nullable(String),
dropoff Nullable(String),
cab_type Nullable(String),
precipitation Nullable(Int8),
snow_depth Nullable(Int8),
snowfall Nullable(Int8),
max_temperature Nullable(Int8),
min_temperature Nullable(Int8),
average_wind_speed Nullable(Int8),
pickup_nyct2010_gid Nullable(Int8),
pickup_ctlabel Nullable(String),
pickup_borocode Nullable(Int8),
pickup_boroname Nullable(String),
pickup_ct2010 Nullable(String),
pickup_boroct2010 Nullable(String),
pickup_cdeligibil Nullable(FixedString(1)),
pickup_ntacode Nullable(String),
pickup_ntaname Nullable(String),
pickup_puma Nullable(String),
dropoff_nyct2010_gid Nullable(UInt8),
dropoff_ctlabel Nullable(String),
dropoff_borocode Nullable(UInt8),
dropoff_boroname Nullable(String),
dropoff_ct2010 Nullable(String),
dropoff_boroct2010 Nullable(String),
dropoff_cdeligibil Nullable(String),
dropoff_ntacode Nullable(String),
dropoff_ntaname Nullable(String),
dropoff_puma Nullable(String)
) ENGINE = MergeTree(pickup_date, pickup_datetime, 8192);
Затем я позабочусь о том, чтобы первый сервер мог видеть все три узла в кластере.
SELECT *
FROM system.clusters
WHERE cluster = 'perftest_3shards'
FORMAT Vertical;
Row 1:
──────
cluster: perftest_3shards
shard_num: 1
shard_weight: 1
replica_num: 1
host_name: 172.30.2.192
host_address: 172.30.2.192
port: 9000
is_local: 1
user: default
default_database:
Row 2:
──────
cluster: perftest_3shards
shard_num: 2
shard_weight: 1
replica_num: 1
host_name: 172.30.2.162
host_address: 172.30.2.162
port: 9000
is_local: 0
user: default
default_database:
Row 3:
──────
cluster: perftest_3shards
shard_num: 3
shard_weight: 1
replica_num: 1
host_name: 172.30.2.36
host_address: 172.30.2.36
port: 9000
is_local: 0
user: default
default_database:
Затем я определю новую таблицу на первом сервере, которая основана на схеме
trips_mergetree_third
и использует движок Distributed.CREATE TABLE trips_mergetree_x3
AS trips_mergetree_third
ENGINE = Distributed(perftest_3shards,
default,
trips_mergetree_third,
rand());
Затем я скопирую данные из таблицы на основе MergeTree на все три сервера. Следующее выполнено за 34 минуты и 44 секунды.
INSERT INTO trips_mergetree_x3
SELECT * FROM trips_mergetree;
После приведенной выше операции я дал ClickHouse 15 минут, чтобы отойти от отметки максимального уровня хранилища. Каталоги данных в конечном итоге составляли 264 ГБ, 34 ГБ и 33 ГБ соответственно на каждом из трех серверов.
Оценка производительности кластера ClickHouse
То, что я увидел дальше, было самым быстрым временем, которое я видел при многократном выполнении каждого запроса в таблице
trips_mergetree_x3
.$ clickhouse-client --host=0.0.0.0
Следующее выполнено за 2.449 секунды.
SELECT cab_type, count(*)
FROM trips_mergetree_x3
GROUP BY cab_type;
Следующее выполнено за 0.691 секунды.
SELECT passenger_count,
avg(total_amount)
FROM trips_mergetree_x3
GROUP BY passenger_count;
Следующее выполнено за 0.,582 секунды.
SELECT passenger_count,
toYear(pickup_date) AS year,
count(*)
FROM trips_mergetree_x3
GROUP BY passenger_count,
year;
Следующее выполнено за 0.983 секунды.
SELECT passenger_count,
toYear(pickup_date) AS year,
round(trip_distance) AS distance,
count(*)
FROM trips_mergetree_x3
GROUP BY passenger_count,
year,
distance
ORDER BY year,
count(*) DESC;
Для сравнения я выполнил те же запросы в таблице на основе MergeTree, которая находится исключительно на первом сервере.
Оценка производительности одного узла ClickHouse
То, что я увидел дальше, было самым быстрым временем, которое я видел при многократном выполнении каждого запроса в таблице
trips_mergetree_x3
.Следующее выполнено за 0.241 секунды.
SELECT cab_type, count(*)
FROM trips_mergetree
GROUP BY cab_type;
Следующее выполнено за 0.826 секунды.
SELECT passenger_count,
avg(total_amount)
FROM trips_mergetree
GROUP BY passenger_count;
Следующее выполнено за 1.209 секунды.
SELECT passenger_count,
toYear(pickup_date) AS year,
count(*)
FROM trips_mergetree
GROUP BY passenger_count,
year;
Следующее выполнено за 1.781 секунды.
SELECT passenger_count,
toYear(pickup_date) AS year,
round(trip_distance) AS distance,
count(*)
FROM trips_mergetree
GROUP BY passenger_count,
year,
distance
ORDER BY year,
count(*) DESC;
Размышления о результатах
Это первый раз, когда бесплатная база данных на базе процессора смогла превзойти базу данных на основе GPU в моих тестах. Та база данных на основе GPU с тех пор подверглась двум ревизиям, но, тем не менее, производительность, которую ClickHouse показал на одном узле, очень впечатляет.
При этом при выполнении Query 1 на распределенном движке накладные расходы оказываются на порядок выше. Я надеюсь, что что-то пропустил в своем исследовании для этого поста, потому что было бы хорошо увидеть, как время запросов снижается, когда я добавляю больше узлов в кластер. Однако это замечательно, что при выполнении других запросов производительность выросла примерно в 2 раза.
Было бы неплохо, если бы ClickHouse развивался в направлении того, чтобы можно было отделить хранилище и вычисления, чтобы они могли масштабироваться независимо. Поддержка HDFS, которая была добавлена в прошлом году, может стать шагом к этому. Что касается вычислений, если один запрос может быть ускорен при добавлении большего количества узлов в кластер, то будущее этого программного обеспечения будет очень безоблачным.
Спасибо, что нашли время, чтобы прочитать этот пост. Я предлагаю консалтинг, архитектуру и услуги по практическому развитию для клиентов в Северной Америке и Европе. Если вы хотите обсудить, как мои предложения могут помочь вашему бизнесу, свяжитесь со мной через LinkedIn.