跳到主要内容
跳到主要内容
编辑此页面

为什么 ClickHouse 如此之快?

除了其数据方向之外,还有许多其他因素会影响数据库的性能。接下来,我们将更详细地解释是什么让 ClickHouse 如此之快,特别是与其他列式数据库相比。

从架构角度来看,数据库(至少)由存储层和查询处理层组成。存储层负责保存、加载和维护表数据,而查询处理层执行用户查询。与其他数据库相比,ClickHouse 在存储层和查询处理层都提供了创新,从而实现了极快的插入和 Select 查询。

存储层:并发插入彼此隔离

在 ClickHouse 中,每个表由多个“表部件”组成。每当用户向表中插入数据(INSERT 语句)时,就会创建一个部件。查询始终针对查询开始时存在的所有表部件执行。

为了避免积累过多的部件,ClickHouse 在后台运行合并操作,持续将多个(小的)部件合并成一个更大的部件。

这种方法有几个优点:一方面,单个插入是“本地”的,因为它们不需要更新全局的,即每个表的数据结构。因此,多个同时插入不需要相互同步或与现有表数据同步,因此插入可以几乎以磁盘 I/O 的速度执行。

存储层:并发插入和选择是隔离的

另一方面,合并部件是一个后台操作,用户不可见,即不影响并发的 SELECT 查询。事实上,这种架构有效地隔离了插入和选择,以至于许多其他数据库也采用了它。

存储层:合并时计算

与其他数据库不同,ClickHouse 还能够在合并操作期间执行额外的数据转换。这方面的例子包括

  • 替换合并,它仅保留输入部件中行的最新版本,并丢弃所有其他行版本。替换合并可以被认为是合并时的清理操作。

  • 聚合合并,它将输入部件中的中间聚合状态组合到新的聚合状态。虽然这似乎难以理解,但它实际上只是实现了增量聚合。

  • TTL(生存时间)合并,根据某些基于时间的规则压缩、移动或删除行。

这些转换的目的是将工作(计算)从用户查询运行时转移到合并时。这对于两个原因很重要

一方面,如果用户查询可以利用“转换”后的数据(例如预聚合数据),则查询速度可能会显着提高,有时甚至提高 1000 倍或更多。

另一方面,合并的大部分运行时都消耗在加载输入部件和保存输出部件上。在合并期间转换数据的额外工作通常不会对合并的运行时产生太大影响。所有这些魔力都是完全透明的,并且不会影响查询的结果(除了它们的性能)。

存储层:数据修剪

在实践中,许多查询是重复的,即在周期性间隔内运行不变或仅稍作修改(例如不同的参数值)。一遍又一遍地运行相同或相似的查询允许添加索引或以可以更快地访问频繁查询的方式重新组织数据。这种方法也称为“数据修剪”,ClickHouse 提供了三种技术来实现这一点

  1. 主键索引,它定义了表数据的排序顺序。一个精心选择的主键允许使用快速二分查找而不是全列扫描来评估过滤器(如上述查询中的 WHERE 子句)。更技术地说,扫描的运行时变为对数而不是数据大小的线性函数。

  2. 表投影作为表的替代内部版本,存储相同的数据,但按不同的主键排序。当存在多个频繁的过滤条件时,投影可能很有用。

  3. 跳过索引,它将额外的数据统计信息嵌入到列中,例如最小和最大列值、唯一值集等。跳过索引与主键和表投影正交,并且根据列中的数据分布,它们可以大大加快过滤器评估速度。

所有这三种技术的目的都是在全列读取期间跳过尽可能多的行,因为读取数据的最快方法是不读取它。

存储层:数据压缩

除此之外,ClickHouse 的存储层还额外(可选地)使用不同的编解码器压缩原始表数据。

列式存储特别适合这种压缩,因为相同类型和数据分布的值位于一起。

用户可以指定使用各种通用压缩算法(如 ZSTD)或专用编解码器(例如用于浮点值的 Gorilla 和 FPC,用于整数值的 Delta 和 GCD,甚至 AES 作为加密编解码器)来压缩列。

数据压缩不仅减小了数据库表的大小,而且在许多情况下,它还提高了查询性能,因为本地磁盘和网络 I/O 通常受到低吞吐量的限制。

最先进的查询处理层

最后,ClickHouse 使用向量化查询处理层,尽可能并行化查询执行,以利用所有资源来实现最大速度和效率。

“向量化”意味着查询计划运算符以批处理而不是单行方式传递中间结果行。这可以更好地利用 CPU 缓存,并允许运算符应用 SIMD 指令一次处理多个值。事实上,许多运算符都有多个版本 - 每个 SIMD 指令集一代一个版本。ClickHouse 将根据其运行硬件的功能自动选择最新和最快的版本。

现代系统有数十个 CPU 核心。为了利用所有核心,ClickHouse 将查询计划展开为多个通道,通常每个核心一个通道。每个通道处理表数据的不相交范围。这样,数据库的性能随着可用核心数量“垂直”扩展。

如果单个节点变得太小而无法容纳表数据,则可以添加更多节点以形成集群。表可以拆分(“分片”)并分布在节点之间。ClickHouse 将在存储表数据的所有节点上运行查询,从而随着可用节点数量“水平”扩展。

对细节的一丝不苟的关注

“ClickHouse 是一个怪异的系统 - 你们有 20 个版本的哈希表。你们有所有这些令人惊叹的东西,而大多数系统只有一个哈希表 …… ClickHouse 具有如此惊人的性能,因为它拥有所有这些专门的组件” Andy Pavlo,CMU 数据库教授

ClickHouse 与众不同之处在于其对低级优化的细致关注。构建一个可以简单工作的数据库是一回事,但对其进行工程设计以在不同的查询类型、数据结构、分布和索引配置中提供速度才是“怪异系统”艺术性闪耀的地方。

哈希表。 让我们以哈希表为例。哈希表是连接和聚合使用的中心数据结构。作为程序员,需要考虑以下设计决策

  • 要选择的哈希函数,
  • 冲突解决:开放寻址链式
  • 内存布局:一个数组用于键和值,还是单独的数组?
  • 填充因子:何时以及如何调整大小?在调整大小期间如何移动值?
  • 删除:哈希表是否应允许驱逐条目?

第三方库提供的标准哈希表在功能上可以工作,但速度不会很快。出色的性能需要细致的基准测试和实验。

ClickHouse 中的哈希表实现根据查询和数据的具体情况选择 30 多种预编译的哈希表变体之一

算法。 算法也是如此。例如,在排序中,您可能会考虑

  • 将要排序的内容:数字、元组、字符串还是结构?
  • 数据是否在 RAM 中?
  • 是否需要排序是稳定的?
  • 应该对所有数据进行排序,还是部分排序就足够了?

依赖于数据特征的算法通常比其通用对应算法表现更好。如果事先不知道数据特征,系统可以尝试各种实现,并选择在运行时效果最佳的实现。例如,请参阅关于 ClickHouse 中如何实现 LZ4 解压缩的文章

VLDB 2024 论文

在 2024 年 8 月,我们的第一篇研究论文被 VLDB 接受并发表。VLDB 是一个关于超大型数据库的国际会议,被广泛认为是数据管理领域的领先会议之一。在数百篇投稿中,VLDB 的总体接受率约为 20%。

您可以阅读论文的 PDF 版本,其中简明扼要地描述了 ClickHouse 最有趣的架构和系统设计组件,这些组件使其如此之快。

我们的 CTO 和 ClickHouse 的创建者 Alexey Milovidov 展示了这篇论文(幻灯片在此),随后是问答环节(很快就超时了!)。您可以在此处观看录制的演示