Terjemahan artikel disiapkan khusus untuk siswa kursus Data Engineer .
ClickHouse adalah basis data kolom sumber terbuka. Ini adalah lingkungan yang hebat di mana ratusan analis dapat dengan cepat meminta data terperinci, bahkan ketika puluhan miliar entri baru diperkenalkan per hari. Biaya infrastruktur untuk mendukung sistem seperti itu dapat mencapai 100 ribu dolar AS per tahun, dan berpotensi setengahnya, tergantung penggunaan. Di beberapa titik, instalasi Yandex.Metrica ClickHouse berisi 10 triliun entri. Selain Yandex, ClickHouse juga mendapatkan kesuksesan dengan Bloomberg dan Cloudflare.
Dua tahun yang lalu, saya melakukan
analisis komparatif terhadap basis data menggunakan satu mesin, dan itu menjadi perangkat lunak basis data
tercepat gratis yang pernah saya lihat. Sejak itu, pengembang tidak berhenti menambahkan fitur, termasuk dukungan untuk kompresi Kafka, HDFS dan ZStandard. Tahun lalu, mereka menambahkan dukungan untuk metode kompresi berjenjang, dan penyandian
delta-delta menjadi mungkin. Saat mengompresi data deret waktu, nilai ukur dapat dikompresi dengan baik menggunakan delta coding, tetapi akan lebih baik menggunakan delta-delta coding untuk penghitung. Kompresi yang baik telah menjadi kunci kinerja ClickHouse.
ClickHouse terdiri dari 170 ribu baris kode C ++, dengan pengecualian perpustakaan pihak ketiga, dan merupakan salah satu basis data kode terkecil untuk basis data terdistribusi. Sebagai perbandingan, SQLite tidak mendukung distribusi dan terdiri dari 235 ribu baris kode dalam bahasa C. Pada saat penulisan ini, 207 insinyur telah berkontribusi untuk ClickHouse, dan intensitas komitmen baru-baru ini meningkat.
Pada bulan Maret 2017, ClickHouse memulai
log perubahan sebagai cara mudah untuk melacak pengembangan. Mereka juga membagi file dokumentasi monolitik menjadi hirarki file berbasis-Markdown. Masalah dan fitur dilacak melalui GitHub, dan secara keseluruhan perangkat lunak ini menjadi lebih mudah diakses dalam beberapa tahun terakhir.
Pada artikel ini, saya akan melihat kinerja klaster ClickHouse pada AWS EC2 menggunakan prosesor 36-core dan drive NVMe.
UPDATE: Seminggu setelah publikasi awal posting ini, saya menjalankan kembali tes dengan konfigurasi yang ditingkatkan dan mencapai hasil yang jauh lebih baik. Posting ini telah diperbarui untuk mencerminkan perubahan ini.
Memulai Cluster AWS EC2
Saya akan menggunakan tiga contoh c5d.9xlarge EC2 untuk posting ini. Masing-masing berisi 36 CPU virtual, 72 GB RAM, 900 GB NVMe SSD dan mendukung jaringan 10-gigabit. Harganya $ 1.962 / jam masing-masing di wilayah barat-1 ketika diluncurkan atas permintaan. Saya akan menggunakan Ubuntu Server 16.04 LTS sebagai sistem operasinya.
Firewall dikonfigurasi sehingga setiap mesin dapat berkomunikasi satu sama lain tanpa batasan, dan hanya alamat IPv4 saya yang masuk daftar putih di kluster SSH.
NVMe yang siap digunakan
Agar ClickHouse berfungsi, saya akan membuat sistem file EXT4 di setiap server di drive NVMe.
$ sudo mkfs -t ext4 /dev/nvme1n1 $ sudo mkdir /ch $ sudo mount /dev/nvme1n1 /ch
Setelah semuanya dikonfigurasi, Anda dapat melihat titik pemasangan dan ruang 783 GB yang tersedia di masing-masing sistem.
$ 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
Dataset yang akan saya gunakan dalam tes ini adalah data dump yang saya hasilkan dari 1,1 miliar taksi yang dibuat di New York dalam enam tahun. Blog
Billion Taxi di Redshift merinci bagaimana saya mengumpulkan kumpulan data ini. Mereka disimpan di AWS S3, jadi saya akan mengkonfigurasi antarmuka baris perintah AWS menggunakan akses saya dan kunci pribadi.
$ sudo apt update $ sudo apt install awscli $ aws configure
Saya akan menetapkan batas jumlah permintaan klien simultan ke 100 sehingga file dimuat lebih cepat daripada dengan pengaturan standar.
$ aws configure set \ default.s3.max_concurrent_requests \ 100
Saya akan mengunduh dataset naik taksi dari AWS S3 dan menyimpannya di drive NVMe di server pertama. Dataset ini ~ 104 GB dalam format CSV terkompresi GZIP.
$ sudo mkdir -p /ch/csv $ sudo chown -R ubuntu /ch/csv $ aws s3 sync s3://<bucket>/csv /ch/csv
Instal ClickHouse
Saya akan menginstal distribusi OpenJDK untuk Java 8, karena diperlukan untuk menjalankan Apache ZooKeeper, yang diperlukan untuk instalasi ClickHouse yang didistribusikan di ketiga mesin.
$ sudo apt update $ sudo apt install \ openjdk-8-jre \ openjdk-8-jdk-headless
Kemudian saya mengatur
JAVA_HOME
lingkungan
JAVA_HOME
.
$ sudo vi /etc/profile export JAVA_HOME=/usr $ source /etc/profile
Kemudian saya akan menggunakan sistem manajemen paket di Ubuntu untuk menginstal ClickHouse 18.16.1, pandangan dan ZooKeeper pada ketiga mesin.
$ sudo apt-key adv \
$ sudo apt install \ clickhouse-client \ clickhouse-server \ glances \ zookeeperd
Saya akan membuat direktori untuk ClickHouse dan juga membuat beberapa konfigurasi tertimpa pada ketiga server.
$ 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
Ini adalah konfigurasi override yang akan saya gunakan.
<?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>
Lalu saya akan menjalankan ZooKeeper dan server ClickHouse di ketiga mesin.
$ sudo /etc/init.d/zookeeper start $ sudo service clickhouse-server start
Memuat data ke ClickHouse
Pada server pertama, saya akan membuat tabel perjalanan, yang akan menyimpan set data naik taksi menggunakan mesin Log.
$ clickhouse-client
Lalu saya membongkar dan memuat masing-masing file CSV ke tabel perjalanan. Berikut ini dilakukan dalam 55 menit dan 10 detik. Setelah operasi ini, ukuran direktori data adalah 134 GB.
$ time (for FILENAME in /ch/csv/trips_x*.csv.gz; do echo $FILENAME gunzip -c $FILENAME | \ clickhouse-client \
Kecepatan impor adalah 155 MB konten CSV terkompresi per detik. Saya menduga ini adalah karena hambatan dalam dekompresi GZIP. Mungkin lebih cepat untuk unzip semua file gzip secara paralel menggunakan xargs dan kemudian memuat data yang tidak di-zip. Berikut ini adalah uraian tentang apa yang dilaporkan selama proses impor 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
Saya akan mengosongkan ruang pada drive NVMe dengan menghapus file CSV sumber sebelum melanjutkan.
$ sudo rm -fr /ch/csv
Konversi ke Bentuk Kolom
Mesin Log ClickHouse akan menyimpan data dalam format berorientasi baris. Untuk meminta data lebih cepat, saya mengonversinya ke format kolom menggunakan mesin MergeTree.
$ clickhouse-client
Berikut ini dilakukan dalam 34 menit dan 50 detik. Setelah operasi ini, ukuran direktori data adalah 237 GB.
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;
Ini adalah apa yang tampak seperti output selama operasi:
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
Dalam tes terakhir, beberapa kolom dikonversi dan diceritakan ulang. Saya menemukan bahwa beberapa fungsi ini tidak lagi berfungsi dengan baik dalam dataset ini. Untuk mengatasi masalah ini, saya menghapus fungsi yang tidak pantas dan mengunduh data tanpa konversi ke tipe yang lebih rinci.
Distribusi data cluster
Saya akan mendistribusikan data di ketiga node cluster. Untuk mulai dengan, saya akan membuat tabel pada ketiga mesin.
$ clickhouse-client
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);
Kemudian saya akan memastikan bahwa server pertama dapat melihat ketiga node di cluster.
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:
Kemudian saya akan mendefinisikan tabel baru pada server pertama, yang didasarkan pada
trips_mergetree_third
dan menggunakan mesin Terdistribusi.
CREATE TABLE trips_mergetree_x3 AS trips_mergetree_third ENGINE = Distributed(perftest_3shards, default, trips_mergetree_third, rand());
Kemudian saya akan menyalin data dari tabel berdasarkan MergeTree ke ketiga server. Berikut ini dilakukan dalam 34 menit dan 44 detik.
INSERT INTO trips_mergetree_x3 SELECT * FROM trips_mergetree;
Setelah operasi di atas, saya memberi ClickHouse 15 menit untuk menjauh dari tanda level penyimpanan maksimum. Direktori data masing-masing mencapai 264 GB, 34 GB, dan 33 GB, di masing-masing dari tiga server.
Penilaian Kinerja Cluster ClickHouse
Apa yang saya lihat berikutnya adalah waktu tercepat yang saya lihat ketika mengeksekusi setiap kueri beberapa kali dalam tabel
trips_mergetree_x3
.
$ clickhouse-client
Berikut ini selesai dalam 2,449 detik.
SELECT cab_type, count(*) FROM trips_mergetree_x3 GROUP BY cab_type;
Berikut ini selesai dalam 0,691 detik.
SELECT passenger_count, avg(total_amount) FROM trips_mergetree_x3 GROUP BY passenger_count;
Berikut ini selesai dalam 0. 582 detik.
SELECT passenger_count, toYear(pickup_date) AS year, count(*) FROM trips_mergetree_x3 GROUP BY passenger_count, year;
Berikut ini selesai dalam 0,983 detik.
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;
Sebagai perbandingan, saya melakukan kueri yang sama dalam sebuah tabel berdasarkan MergeTree, yang terletak secara eksklusif di server pertama.
Penilaian kinerja satu simpul ClickHouse
Apa yang saya lihat selanjutnya adalah waktu tercepat yang saya lihat ketika mengeksekusi setiap query beberapa kali dalam tabel
trips_mergetree_x3
.
Berikut ini selesai dalam 0,241 detik.
SELECT cab_type, count(*) FROM trips_mergetree GROUP BY cab_type;
Berikut ini selesai dalam 0,826 detik.
SELECT passenger_count, avg(total_amount) FROM trips_mergetree GROUP BY passenger_count;
Berikut ini selesai dalam 1,209 detik.
SELECT passenger_count, toYear(pickup_date) AS year, count(*) FROM trips_mergetree GROUP BY passenger_count, year;
Berikut ini selesai dalam 1,781 detik.
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;
Refleksi hasil
Ini adalah pertama kalinya basis data berbasis prosesor gratis mampu mengungguli basis data GPU dalam pengujian saya. Basis data berbasis GPU itu telah mengalami dua revisi sejak saat itu, tetapi, bagaimanapun, kinerja yang ditunjukkan ClickHouse pada satu simpul sangat mengesankan.
Pada saat yang sama, ketika menjalankan Kueri 1 pada mesin terdistribusi, biaya overhead adalah urutan besarnya lebih tinggi. Saya berharap bahwa saya melewatkan sesuatu dalam penelitian saya untuk posting ini, karena akan menyenangkan untuk melihat bagaimana waktu kueri berkurang ketika saya menambahkan lebih banyak node ke cluster. Namun, adalah luar biasa bahwa ketika melakukan permintaan lain, produktivitas meningkat sekitar 2 kali lipat.
Akan lebih baik jika ClickHouse berkembang sehingga memungkinkan untuk memisahkan penyimpanan dan komputasi sehingga mereka dapat menskalakan secara mandiri. Dukungan HDFS, yang ditambahkan tahun lalu, bisa menjadi langkah menuju ini. Sedangkan untuk komputasi, jika satu permintaan dapat dipercepat dengan menambahkan lebih banyak node ke cluster, maka masa depan perangkat lunak ini akan sangat cerah.
Terima kasih telah meluangkan waktu untuk membaca posting ini. Saya menawarkan layanan konsultasi, arsitektur, dan pengembangan langsung untuk klien di Amerika Utara dan Eropa. Jika Anda ingin membahas bagaimana saran saya dapat membantu bisnis Anda, hubungi saya melalui
LinkedIn .