本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
在存储数据超过20万亿 行的情况下,ClickHouse做到了90 %的查询都能够在1秒内返回的惊人之举 ------《ClickHouse原理解析与应用实践》
怎么个事儿
不知道大家有没有听过ClickHouse
,反正我在第一次听到ClickHouse
之前都是没有听过ClickHouse
的
因为我现在的公司有用ClickHouse
来实现一些业务场景,但我却没听说过,所以特地问了几个前同事看看他们是不是对ClickHouse
有所了解
同事A:那是个啥?
同事B:刷到过,但是没用过。
同事C:你要请我吃饭?
他们纷纷表示没有听过或者听过但不熟或者。。。要请我吃饭
很好,ClickHouse
你已经成功的引起了我的注意,毕竟有人说要请我吃饭(dog
定位
ClickHouse
是一款OLAP
数据库
什么是OLAP
?
On-Line Analysis Processing,联机分析处理,比如Hive
与之对应还有一种OLTP
On-Line Transaction Processing,联机事务处理,比如MySQL
简而言之,ClickHouse
就是一款大数据分析数据库
适用的场景
ClickHouse
非常适合大宽表,就是字段较多的表
不同纬度的数据存储在一张表中更方便做纬度分析
分析(营销)
借助用户的行为画像,对用户进行精准的内容投放
假设我们开了一家淘宝店铺,最近这段时间我们的产品A会进行打折促销
同时我们可以认为之前复购过产品A的顾客有很大的可能会再次回购
所以我们可以圈定产品A购买次数 > 1
的顾客
给他们发送短信或推送消息,告诉他们之前购买过的产品A最近有活动
当然实际的行为分析字段非常多而且营销条件也会更复杂
为什么
所以为什么ClickHouse
适合这种分析场景呢
当然是因为快啊!
假设我们有1亿个客户,有100个可以用于用户行为筛选的字段
你:MySQL大哥,能不能。。。
MySQL:滚!
列式存储
和我们熟悉的按行存储的MySQL
不同,ClickHouse
是按列存储的
所以ClickHouse
可以按需读取数据
我们继续来开淘宝店
我们的客户行为表有非常多的字段,比如下表(非真实字段):
客户号 | 新客户 | 店铺点击次数 | 总花费金额 | 产品A购买次数 | 点击产品A详情页面次数 | ...共100个字段 |
---|---|---|---|---|---|---|
1 | 否 | 123 | 1378.24 | 3 | 27 | ... |
2 | 否 | 56 | 2567.98 | 5 | 5 | ... |
... | ... | ... | ... | ... | ... | ... |
100000 | 是 | 3 | 0.00 | 0 | 1 | ... |
现在我们要开始营销了
sql
select 客户号 from 客户行为表 where 产品A购买次数 > 1
如果用MySQL
,因为是按行存储,所以每一行的所有数据都要读取出来作判断
也就是10w行 * 100个字段
如果用ClickHouse
,因为是按列存储,而我们的SQL
中只需要用到两列,所以只需要读取两列即可
也就是10w行 * 2个字段
大量无用数据的磁盘IO被优化掉了,毕竟磁盘IO的效率真的非常低
数据压缩
除了优化无用数据的磁盘IO,ClickHouse
还使用了数据压缩来优化有用数据的磁盘IO
由于同一列数据的数据类型一致,所以数据更容易压缩,压缩效率也更高
一般会以8192 行数据作为一块(最小处理单元)进行压缩和解压
一条一条数据压缩解压性能很低,毕竟压缩和解压也需要时间,频繁的压缩解压反而不如不压缩
从书中了解到,ClickHouse
一般能达到6-8:1的压缩比,也就是6G-8G的数据压缩之后就变成了1G
让我们浅浅估算一下我们的营销SQL
:
假设MySQL
和ClickHouse
都要扫描10w
行数据,每列数据的数据量一样,压缩率也一样
光是磁盘IO的效率就能至少提升100列/2列 * 6/1(压缩)= 300倍
向量计算
你以为到这儿就结束了?
还有一项降维打击的优化策略,那就是向量计算
向量计算 需要借助CPU
的SIMD
技术
Single Instruction Multiple Data,单指令多数据流
使用单条指令对多个数据进行并行操作
一般情况下,我们通过循环来处理多条数据(假设有4条数据)
js
for (int i = 0;i < 4; i++) {
c[i] = cmd(a[i], b[i]);
}
这段代码最终执行的时候就会变成下面这样,实际上会执行4次
js
c[0] = cmd(a[0], b[0]);
c[1] = cmd(a[1], b[1]);
c[2] = cmd(a[2], b[2]);
c[3] = cmd(a[3], b[3]);
而向量计算则是这样的
js
c[] = cmd(a[], b[]);
举一个更具体的例子就是(以两数相加为例)
js
[3, 5, 7, 9] = [1, 2, 3, 4] + [2, 3, 4, 5]
四条数据只需要执行一次相加指令
对于不同的数据类型(8位,16位,32位,64位),SIMD
可以同时处理2-16个数据
所以对于列式存储的ClickHouse
来说,可以非常方便的使用向量计算 提升2-16倍(粗略计算)的计算效率
位图计算
顺带提一句,ClickHouse
是支持位图存储和计算的
所以在部分场景的性能非常高,比如一些需要用到IN
的场景
IN
一百万的过滤和用位图判断是否存在,这两者的性能差距还是很大的
位图相关的内容会在后续的文章中做单独的说明
不适用的场景
单条数据
因为列式存储 而且每次处理8192行数据
为了一条数据读取8192条数据怎么想都不划算吧
而且查询的列越多越不划算,列式存储反而变成了弊端
不如换成行式存储的数据库
不支持事务
这个应该也好理解
如果要支持一条数据的事务能力,就需要读取每一列的数据,相比单条数据更是雪上加霜
再加上需要考虑并发竞争,性能必定直线下降
所以需要事务的场景还是交给OLTP
数据库吧
兼容标准SQL
ClickHouse
的语法和我们常用的SQL
基本没有太大的差别
如果熟悉MySQL
的话,完全不会有任何问题(当然还是有一些细小的差别)
并不会像ES
那样有自己的一套DSL
需要单独学习
所以把一些大表迁移到ClickHouse
之后查询SQL
也是基本兼容的,迁移后重新开发的成本也不高
小结
ClickHouse
非常适合大宽表,也就是字段(纬度)非常多,数据量非常大的实时统计分析查询
但是不适合事务场景以及单条(精准)查询