ClickHouse功能非常丰富。 它支持16个不同的表引擎,它包括动画进度指示器和语法高亮显示,并且它的性能在开源产品中出类拔萃。
这就是说,这个软件并不是像PostgreSQL这样的替代品。 它缺少事务,完整的UPDATE和DELETE语句,像DROP TABLE这样的调用不能在超过54 GB的表上工作,而无需先改变服务器的配置。 尽管如此,它还是一个非常值得敬重的实时大数据分析处理平台。
Yandex的高级软件工程师Alexey Milovidov和ClickHouse的核心贡献者之一,编写了一个基准指南,在基准测试中使用的11亿辆出租车数据集来对数据库进行基准测试。
在这个博客文章中,我将使用我曾经用来对Amazon Athena,BigQuery,Elasticsearch,kdb + / q,MapD,PostgreSQL,Presto,Redshift,Spark和Vertica进行基准测试的完全相同的集合来运行ClickHouse的基准测试。 我编制了这些基准的单页总结。
硬件
在这个基准测试中,我将运行一个全新安装的Ubuntu 14.04.3 LTS,主频为3.4 GHz的Intel Core i5 4670K,16 GB DDR3 RAM和SanDisk SDSSDHII960G 960 GB SSD硬盘。
ClickHouse 启动和运行
我将首先运行一些命令,以便从预先制作的包中安装ClickHouse。
$echo"deb http://repo.yandex.ru/clickhouse/trusty stable main"|\sudo tee -a /etc/apt/sources.list
$ sudo apt-key adv\--keyserver keyserver.ubuntu.com\--recv E0C56BD4
$ sudo apt-get update
然后,我将安装客户端和服务器软件的版本1.1.54140。
$ sudo apt-get install\clickhouse-client\clickhouse-server-common
然后我将启动服务器。 我还没有设置任何定制的配置,所以所有的设置是你开箱即用的。
$ sudo service clickhouse-server start
加载 11亿的Trips数据进入ClickHouse
我正在使用的数据集被分成56个gzip压缩的CSV文件。 我首先将它们导入由“Log”引擎支持的“Trips”表。
$ clickhouse-client
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;
数据集本身使用逗号分隔字段。 数据的内容都不包含任何逗号,所以没有引用来帮助转义数据。 NULL值由逗号分隔符之间简单的没有任何内容来定义。 通常情况下这不是问题,但ClickHouse空字段不会被视为NULL,以避免空字符串的歧义。 出于这个原因,我需要通过一个转换脚本来管理数据,将用 \N替换所有的空值。
以下是转换脚本。
$ cat trans.py
import sys
for line in sys.stdin:
print ','.join([item if len(item.strip())else '\N'
for item in line.strip().split(',')])
这是我用来将11亿条记录导入Trips表的bash命令。
$ time(for filename in /home/mark/trips/trips_x*.csv.gz;do
gunzip -c $filename | \
python trans.py | \
clickhouse-client \
--query="INSERT INTO trips FORMAT CSV"
done)
执行上述脚本大约3小时27分35秒。 毫无疑问,如果我没有使用Python脚本来转换内容,那么这个时间就会大大减少。 以下显示了导入过程中的顶层命令,您可以看到Python脚本耗尽了大量资源。
... %CPU %MEM TIME+ COMMAND
... 99.2 0.1 0:32.76 python trans.py
... 45.3 12.3 0:17.14 clickhouse-client --query=INSERT INTO trips FORMAT CSV
... 28.3 0.0 0:09.89 gzip -d -c /home/mark/trips/trips_xac.csv.gz
使用日志引擎支持的表,我将创建一个使用MergeTree引擎的新表。 这个表格将填充Trips表的内容。
在Milovidov的指导中,这个表的一些字段从字符串转换成枚举列。 其中一些列不支持在我的数据集版本中看到的每个值,所以我需要删除这些字段的转换过程。 下面是我的版本的MergeTree支持表。
下面39分57秒完成。
CREATETABLEtrips_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) IN ('Y', '1', '2') 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,
CAST((assumeNotNull(payment_type) AS pt) IN ('CSH', 'CASH', 'Cash', 'CAS', 'Cas', '1') ? 'CSH' : (pt IN ('CRD', 'Credit', 'Cre', 'CRE', 'CREDIT', '2') ? 'CRE' : (pt IN ('NOC', 'No Charge', 'No', '3') ? 'NOC' : (pt IN ('DIS', 'Dispute', 'Dis', '4') ? 'DIS' : 'UNK'))) AS Enum8('CSH' = 1, 'CRE' = 2, 'UNK' = 0, 'NOC' = 3, 'DIS' = 4)) AS payment_type_,
assumeNotNull(trip_type) AS trip_type,
ifNull(toFixedString(unhex(pickup), 25), toFixedString('', 25)) AS pickup,
ifNull(toFixedString(unhex(dropoff), 25), toFixedString('', 25)) AS dropoff,
CAST(assumeNotNull(cab_type) AS Enum8('yellow' = 1, 'green' = 2, 'uber' = 3)) AS cab_type,
assumeNotNull(pickup_nyct2010_gid) AS pickup_nyct2010_gid,
toFloat32(ifNull(pickup_ctlabel, '0')) AS pickup_ctlabel,
assumeNotNull(pickup_borocode) AS pickup_borocode,
assumeNotNull(pickup_boroname) AS pickup_ct2010,
toFixedString(ifNull(pickup_boroct2010, '0000000'), 7) AS pickup_boroct2010,
assumeNotNull(ifNull(pickup_cdeligibil, ' ')) AS pickup_cdeligibil,
toFixedString(ifNull(pickup_ntacode, '0000'), 4) AS pickup_ntacode,
assumeNotNull(pickup_ntaname) AS pickup_ntaname,
toUInt16(ifNull(pickup_puma, '0')) AS pickup_puma,
assumeNotNull(dropoff_nyct2010_gid) AS dropoff_nyct2010_gid,
toFloat32(ifNull(dropoff_ctlabel, '0')) AS dropoff_ctlabel,
assumeNotNull(dropoff_borocode) AS dropoff_borocode,
assumeNotNull(dropoff_boroname) AS dropoff_ct2010,
toFixedString(ifNull(dropoff_boroct2010, '0000000'), 7) AS dropoff_boroct2010,
assumeNotNull(ifNull(dropoff_cdeligibil, ' ')) AS dropoff_cdeligibil,
toFixedString(ifNull(dropoff_ntacode, '0000'), 4) AS dropoff_ntacode,
assumeNotNull(dropoff_ntaname) AS dropoff_ntaname,
toUInt16(ifNull(dropoff_puma, '0')) AS dropoff_puma
FROM trips
ClickHouse 基准测试
对于这个基准测试,我将通过ClickHouse CLI运行SQL命令。 默认情况下,CLI在每个SELECT语句后以毫秒粒度打印计时遥测。
下面引用的时间是一系列运行中查看的最低查询次数。 和我所有的基准一样,我使用最低的查询时间来表示“最高速度”。
$ clickhouse-client
以下在1.034秒内完成。
SELECT cab_type,count(*) FROM trips_mergetree GROUP BY cab_type
以下完成在3.058秒。
SELECT passenger_count,avg(total_amount) FROM trips_mergetree GROUP BY passenger_count
以下在5.354秒完成。
SELECT passenger_count, toYear(pickup_date) AS year,count(*) FROM trips_mergetree GROUP BY passenger_count,year
以下在12.748秒内完成。
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
这些结果把我的基准测试#6位置ClickHouse。 所看到的性能,鉴于这个基准测试的硬件,是令人印象深刻的。
欢迎大家在各种应用场景中尝试使用 ClickHouse,ClickHouse 确实是一个不可多得的开源实时大数据处理平台。