1.1 мільярд поїздок на таксі: 108-ядерний кластер ClickHouse

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

1.1 мільярд поїздок на таксі: 108-ядерний кластер 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 секунди.

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 на розподіленому двигуні накладні витрати виявляються на порядок вищими. Я сподіваюся, що щось пропустив у своєму дослідженні для цієї посади, тому що було б добре побачити, як час запитів знижується, коли я додаю більше вузлів у кластер. Однак це чудово, що при виконанні інших запитів продуктивність зросла приблизно вдвічі.

Було б непогано, якби ClickHouse розвивався у напрямку того, щоб можна було відокремити сховище та обчислення, щоб вони могли масштабуватися незалежно. Підтримка HDFS, яку було додано минулого року, може стати кроком до цього. Що стосується обчислень, якщо один запит може бути прискорений при додаванні більшої кількості вузлів до кластера, то майбутнє цього програмного забезпечення буде дуже безхмарним.

Дякую, що знайшли час, щоб прочитати цей пост. Я пропоную консалтинг, архітектуру та послуги з практичного розвитку для клієнтів у Північній Америці та Європі. Якщо ви хочете обговорити, як мої пропозиції можуть допомогти вашому бізнесу, зв'яжіться зі мною через LinkedIn.

Джерело: habr.com

Додати коментар або відгук