本文为稀土掘金技术社区首发签约文章,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非常适合大宽表,也就是字段(纬度)非常多,数据量非常大的实时统计分析查询
但是不适合事务场景以及单条(精准)查询