ClickHouse向量化执行与Pipeline设计
ClickHouse简介
ClickHouse是一个高效的OLAP数据库,由Yandex公司于2016年开源,现已独立。它是一个工程艺术品,充分利用了现有技术,并进行了极致的性能优化。ClickHouse的核心特性包括:
- True Column-Oriented Storage(真正的列式存储):基于MergeTree(类似LSM Tree)的列式存储,相比其他列式存储具有优势。
- Vectorized Query Execution(向量化查询执行):以Block为单位,按列对数据进行处理,充分发挥CPU的并行计算能力。
向量化执行
- 什么是向量化:对不同的数据执行同样的一个或一批指令,通过CPU数据并行(SIMD)实现,即对数组/向量进行连续操作。
- 硬件支持:Intel CPU提供SSE、AVX等扩展向量化指令集。
- SIMD缺陷:不适用于严重依赖控制流的任务,主要优化可并行计算的简单场景。
- 如何实现向量化:通过Intrinsic函数和编译器自动向量化实现。
- Intrinsic函数:使用SSE_mm_add_ps等Intrinsic函数对关键路径代码进行优化。
- 编译器自动向量化:通过良好的架构设计和代码设计,使编译器能够生成良好的向量化代码,例如使用模板、内联函数、减少虚函数调用等。
- ClickHouse向量化:大量使用Intrinsic函数和编译器自动向量化,例如Plus函数、apply函数等,并通过__restrict关键字限制指针别名,帮助编译器进行优化。
Pipeline设计与实现
- 传统火山模型:Tuple-at-a-time processing,以行为单位处理数据,无法发挥向量化能力,CPUCache不友好,虚函数调用开销大,CPU利用率不高。
- Pipeline执行模型:以Block为单位,按列对数据进行处理,易于实现向量化。
- ClickHouse SQL执行流程:Executor在ExecutingGraph上完成Pipeline的调度执行。
- QueryPlan:由QueryStep构成的Tree,除Join外,实际退化为链表,延迟Pipeline创建,在其上能够进行Pipeline级别的优化。
- Pipeline基本结构:有向无环图,节点为Processor,Port为输入或输出,边为Processor之间的连接。
- Processors:Pipeline的基本构建块,能够对数据进行处理,有Source和Sink两种类型。
- Port:连接Processor,实现数据流通,有Output port和Input port两种类型。
- Processor状态:通过状态迁移实现数据流动,例如Read、Expression、Filter等状态。
- Pipe与Processor的区别:Pipe是Processors的集合,构成pipeline的一部分;Processor是Pipeline的基本构建块。
- Pipeline:从Source开始,到Sink结束,GROUP BY/ORDER BY之前并行执行,之后只有一个流。
- Pipeline执行:通过Processor状态迁移使得数据在Processor之间流水起来,直到所有Processor进入Finished状态,执行完成。
- Pipeline动态更改:创建新的Processor,例如Aggregating函数的两阶段执行。
- Expression计算:通过ExpressionActions实现,表达式分析-> ActionsDAG(由表达式构成的DAG)。
- ActionsDAG:有向无环图,方便表达Expression之间的依赖关系,基于DAG,方便对Action进行优化。
- Pipeline与向量化:Pipeline实现了数据以Block为单位按列执行,是ClickHouse向量化执行的核心。
总结
- 如何提高向量化:
- Writing better code:系统的架构设计、代码设计是影响向量化能力的重要因素,例如使用Pipeline执行模型替代传统火山模型。
- Finding out critical path:针对影响性能的关键代码路径,可以在后期通过Intrinsic函数手动向量化。