为什么 ClickHouse 这么快?
除了数据方向,还有许多其他因素影响数据库的性能。接下来我们将更详细地解释是什么让 ClickHouse 如此快速,特别是与其他列式数据库相比。
从架构的角度来看,数据库至少由存储层和查询处理层组成。存储层负责保存、加载和维护表数据,而查询处理层执行用户查询。与其他数据库相比,ClickHouse 在这两个层面提供了创新,使得插入和 Select 查询非常快速。
存储层:并发插入相互隔离
在 ClickHouse 中,每个表由多个“表片段”组成。用户插入数据时(INSERT 语句),就会创建一个片段。查询始终在查询开始时存在的所有表片段上执行。
为了避免太多片段的积累,ClickHouse 在后台运行合并操作,持续将多个较小的片段合并为一个较大的片段。
这种方法有几个优点:所有数据处理可以卸载到后台片段合并,保持数据写入轻量且高效。单个插入在某种意义上是“本地的”,因为它们不需要更新全局的、即每个表的数据结构。因此,多次同时插入无需彼此同步或与现有表数据同步,因此插入几乎可以以磁盘 I/O 的速度执行。
关于 VLDB 论文的整体性能优化部分。
🤿 深入了解 磁盘格式 部分,具体请参见我们 VLDB 2024 论文的网络版本。
存储层:并发插入和选择相互隔离
插入完全与 SELECT 查询隔离,插入的数据片段的合并在后台进行,而不影响并发查询。
🤿 深入了解 存储层 部分,具体请参见我们 VLDB 2024 论文的网络版本。
存储层:合并时间计算
与其他数据库不同,ClickHouse 通过在合并后台过程中执行所有附加数据转换,保持数据写入轻量和高效。这些转换的例子包括:
-
替换合并,仅保留输入部分中行的最新版本,丢弃所有其他行版本。替换合并可视为合并时清理操作。
-
聚合合并,将输入部分中的中间聚合状态组合成新的聚合状态。尽管这看似难以理解,实际上仅仅是实现递增聚合。
-
生存时间 (TTL) 合并,根据某些基于时间的规则压缩、移动或删除行。
这些转换的重点是在用户查询运行时将计算工作转移到合并时间。这一点重要的原因有两个:
一方面,如果用户查询能够利用“转化过”的数据,例如预聚合的数据,查询运行时间可能会显著加快,有时甚至加快 1000 倍或更多。
另一方面,大多数合并的运行时间被加载输入片段和保存输出片段所消耗。在合并过程中转换数据的额外工作通常不会对合并的运行时间产生太大影响。所有这些魔法都是完全透明的,不影响查询结果(除了性能)。
🤿 深入了解 合并时间数据转换 部分,具体请参见我们 VLDB 2024 论文的网络版本。
存储层:数据修剪
在实践中,许多查询是重复的,即在周期性间隔内以不变或仅轻微修改的方式运行(例如,参数值不同)。一次又一次地运行相同或相似的查询允许添加索引或以某种方式重新组织数据,以便频繁查询能够更快地访问。这种方法也被称为“数据修剪”,ClickHouse 提供了三种技术来实现这一点:
-
主键索引,定义表数据的排序顺序。一个良好选择的主键可以利用快速的二分搜索评估过滤器(如上述查询中的 WHERE 子句),而不需要全列扫描。从更技术的角度看,扫描的运行时间变为相对于数据大小的对数而不是线性。
-
表投影,作为表的替代内部版本,存储相同的数据但按不同的主键排序。当频繁有多个过滤条件时,投影非常有用。
-
跳过索引,将额外的数据统计信息嵌入到列中,例如最小和最大列值、唯一值集合等。跳过索引与主键和表投影是正交的,根据列中的数据分布,它们可以大大加快过滤器的评估速度。
这三种技术的目标是在全列读取中尽可能跳过尽量多的行,因为读取数据的最快方式是根本不读取它。
🤿 深入了解 数据修剪 部分,具体请参见我们 VLDB 2024 论文的网络版本。
存储层:数据压缩
除此之外,ClickHouse 的存储层还(可选地)使用不同的编解码器压缩原始表数据。
列存储特别适合此类压缩,因为相同类型和数据分布的值集中在一起。
用户可以指定,将列使用各种通用压缩算法(如 ZSTD)或专门的编解码器进行压缩,例如 Gorill 和 FPC 用于浮点值,Delta 和 GCD 用于整数值,甚至 AES 作为加密编解码器。
数据压缩不仅减少了数据库表的存储大小,而且在许多情况下,它还改善了查询性能,因为本地磁盘和网络 I/O 通常受到低吞吐量的限制。
🤿 深入了解 磁盘格式 部分,具体请参见我们 VLDB 2024 论文的网络版本。
最先进的查询处理层
最后,ClickHouse 使用向量化查询处理层,尽可能地并行化查询执行,以利用所有资源以获得最大的速度和效率。
“向量化”的意思是查询计划运算符以批量的方式传递中间结果行,而不是逐行传递。这不仅导致更好的 CPU 缓存利用率,还允许运算符使用 SIMD 指令同时处理多个值。事实上,许多运算符都有多个版本-每个 SIMD 指令集生成一个。ClickHouse 将根据其运行的硬件能力自动选择最近且最快的版本。
现代系统有数十个 CPU 核心。为了利用所有核心,ClickHouse 将查询计划展开为多个处理通道,通常为每个核心一个。每个通道处理表数据的一个不重叠范围。这样,数据库的性能随着可用核心数量的增加而“垂直”扩展。
如果单个节点变得太小而无法容纳表数据,可以添加更多节点组成集群。可以将表“分片”并分布在节点之间。ClickHouse 将在所有存储表数据的节点上运行查询,从而随着可用节点数的增加实现“水平”扩展。
🤿 深入了解 查询处理层 部分,具体请参见我们 VLDB 2024 论文的网络版本。
对细节的严谨关注
“ClickHouse 是一个怪异的系统——你们有 20 个版本的哈希表。你们有这些令人惊叹的东西,而大多数系统只会有一个哈希表……ClickHouse 之所以具有如此出色的性能,是因为它有所有这些专门的组件。” Andy Pavlo, 卡内基梅隆大学数据库教授
ClickHouse 的与众不同之处在于对底层优化的严谨关注。构建一个简单可用的数据库是一回事,但工程设计以在各种查询类型、数据结构、分布和索引配置中提供速度,正是“怪异系统”艺术的体现。
哈希表。 以哈希表为例。哈希表是连接和聚合使用的中心数据结构。作为程序员,必须考虑这些设计决策:
由第三方库提供的标准哈希表在功能上是可行的,但速度并不快。出色的性能需要仔细的基准测试和实验。
ClickHouse 中的哈希表实现根据查询和数据的具体情况选择 30 多种预编译哈希表变体 中的一种。
算法。 对于算法也是如此。例如,在排序时,你可能会考虑:
- 将要排序的是什么:数字、元组、字符串还是结构?
- 数据是否在 RAM 中?
- 排序是否需要稳定性?
- 是否需要对所有数据进行排序,还是部分排序足够?
依赖于数据特征的算法通常比通用算法表现更佳。如果数据特征事先未知,系统可以尝试多种实现并选择在运行时效果最好的。例如,参见关于 ClickHouse 中 LZ4 解压缩实现的文章。
🤿 深入了解 整体性能优化 部分,具体请参见我们 VLDB 2024 论文的网络版本。
VLDB 2024 论文
在 2024 年 8 月,我们的第一篇研究论文被 VLDB 录用并发表。VLDB 是一个关于超大规模数据库的国际会议,被广泛认为是数据管理领域的领先会议之一。在数百份投稿中,VLDB 通常的录用率为 ~20%。
您可以阅读 论文的 PDF 或我们网络版本,它简要描述了 ClickHouse 最有趣的架构和系统设计组件,哪些使其如此快速。
我们的首席技术官和 ClickHouse 创始人 Alexey Milovidov 介绍了该论文(幻灯片 在这里),随后进行了问答(很快就超时了!)。您可以在这里观看录制的演示: