为啥他能这么快呢?设计如此=-=
概述
ClickHouse设计的初衷是to filter and aggregate data as fast as possible。
再优秀的系统的设计也只能对各个指标进行取舍,无法兼得,而ClickHouse的最重要的指标和任务就是做好这样的一件事:尽可能快的过滤和聚合数据。实际上,这也是传统的OLAP系统所追求的指标。体现到语义上,就是实现GROUP BY查询的实现。
为了实现这一目的,ClickHouse在上层设计上做出了如下的优化:
- Column-oriented storage
- Indexes
- Block
- Pre-sort
- Data compression
- Vectorized query execution
- Scalability
然而许多其他的数据库使用的相似的优化策略,为什么ClickHouse能够脱颖而出呢?另一个值得关注的点是其底层实现细节。一个观点是:ClickHouse并没有太多的新的方法或者理论,而是将前人的研究成果应用到了OLAP这个领域。因此可以说,ClickHouse是一个非常优秀及经典的工程化项目,是人类计算机工程的结晶。
Column-oriented storage
列式存储能够显著的降低磁盘IO。对于OLAP场景中,每次只关心一个大宽表中的某几列的场景,相比于行式存储,列式存储仅需获取需要的数据,磁盘的IO理论上能够降低为所关心的列与所有列之比。因此,越是宽的表,行式存储的优势越明显。
同样,使用ClickHouse时,也一定要使用列式存储数据库的方式来查询数据,查询时指定具体的列,否则性能提升会不明显。
Block
ClickHouse使用Block实现批处理。Block 是ClickHouse中的数据最小处理单元,表示内存中表的子集(chunk)的容器,是由三元组: (IColumn, IDataType, 列名) 构成的集合。在查询执行期间,数据是按 Block 进行处理的。如果我们有一个 Block ,那么就有了数据(在 IColumn 对象中),有了数据的类型信息告诉我们如何处理该列,同时也有了列名(来自表的原始列名,或人为指定的用于临时计算结果的名字)。
当我们遍历一个块中的列进行某些函数计算时,会把结果列加入到块中,但不会更改函数参数中的列,因为操作是不可变的。之后,不需要的列可以从块中删除,但不是修改。这对于消除公共子表达式非常方便。
Pre-sort
ClickHouse会对插入的数据进行预排序(基于LSM算法)。这里设计的原因是针对大数据量场景,为了处理上百亿条记录的数据,一般的查询返回的数据量都非常大,如果数据是无序的,对于按字段聚合或者范围查询的场景,会大大增加磁盘IO次的次数。
to be discussed
这里预排序是对每个字段都预排序吗?如果按照字段A进行范围筛选,获取相应的字段B进行计算,按照ClickHouse的列式存储和预排序,这里的流程是如何实现的?ClickHouse是如何根据排序后的A,找到对应行的B的呢?
Data compression
数据压缩是提升ClickHouse性能的一个关键。我们发现,前面介绍的列存、分块和预排序,实际上都对压缩有着一定的好处,
- 列式的存储使得数据更有规律,可以更好地进行数据压缩(相同类型的数据放在一起,对压缩更加友好);同时,能够最小化数据扫描的范围。
- 排序后的数据可以使用更有效的压缩方式来进行处理。
- 按照block作为最小处理单元的原因是,虽然数据被压缩后能够有效减少数据大小,降低存储空间并加速数据传输效率,但数据的压缩和解压动作,其本身也会带来额外的性能损耗。所以需要控制被压缩数据的大小,以求在性能损耗和压缩率之间寻求一种平衡。在具体读取某一列数据时(.bin文件),首先需要将压缩数据加载到内存并解压,这样才能进行后续的数据处理。通过压缩数据块,可以在不读取整个.bin文件的情况下将读取粒度降低到压缩数据块级别,从而进一步缩小数据读取的范围。
Indexes
由于ClickHouse实现了在插入时进行了预排序,因此其索引做的非常简单,无需像MySQL需要设置单独的索引文件索引数据的位置,因为其数据本身就是有序的。
ClickHouse的索引包括一级索引,标记和二级索引。一级索引记录每一个block第一个值。例如一组一亿行的数据,主键范围从1~100,000,000。存储到ClickHouse后按照8192行为一个block,那么一共有12208个block。索引为1,8193,16635……在查询时只需要就可以根据值确定到需要读取哪几个block了。但是仅仅靠定位到具体哪个block还是不够,因为我们仍不清楚这个block在文件的哪个位置,因此就有了标记来记录block在文件中的偏移量。由于一级索引非常小,1亿条数据只需要1万多行的索引,因此一级索引可以常驻内存,加速查找。
Vectorized query execution
ClickHouse使用C++进行开发,通过向量化的实现进行数据的高效查询。主要包括提升CPU 缓存利用率,以及使用 SIMD CPU 指令。
Clickhouse在所有能够提高CPU计算效率的地方,都大量的使用了SIMD,频繁调用的基础函数,大量的进行可并行计算,将工程上的优化做到了极致。
以下的代码是一个例子,对于一个简单的额大小写转换的方法,

1万+

被折叠的 条评论
为什么被折叠?



