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