Clickhouse 实践之11亿出租车出行信息处理

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 确实是一个不可多得的开源实时大数据处理平台。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容