跳到主要内容
跳到主要内容

架构概览

这是我们VLDB 2024 科学论文的网络版本。我们还博文谈到了其背景和发展历程,并推荐观看 ClickHouse 首席技术官暨创始人 Alexey Milovidov 的 VLDB 2024 演讲:

摘要

在过去的几十年里,存储和分析的数据量呈指数增长。各行各业的企业开始依赖这些数据来改进产品、评估性能和做出影响商业决策的决策。然而,随着数据量的日益增大,企业需要以具有成本效益和可扩展的方式管理历史数据和新数据,同时使用高并发的查询进行分析,并期望实现实时延迟(例如,根据用例需求,少于一秒)。

本文介绍了 ClickHouse 的概述,这是一种流行的开源 OLAP 数据库,旨在对构建于 PB 级数据集上的高性能分析进行处理。其存储层结合了基于传统日志结构合并(LSM)树的数据格式与用于后台历史数据连续转化(如聚合、存档)的一些新技术。查询以方便的 SQL 方言编写,并由最先进的向量化查询执行引擎处理,并可以选择编译代码。ClickHouse 积极利用修剪技术,避免在查询中评估不相关的数据。其他数据管理系统可以在表函数、表引擎或数据库引擎级别集成。现实世界基准测试表明,ClickHouse 是市场上最快的分析数据库之一。

1 引言

本文描述了 ClickHouse, 一个设计用于高性能分析查询的列式 OLAP 数据库,支持包含数万亿行和数百列的表。ClickHouse于2009年作为 web 规模日志文件数据的过滤和聚合操作符启动,并于2016年开源。图 1说明了本文描述的主要功能何时引入 ClickHouse 的。

ClickHouse 的设计旨在解决现代分析数据管理中的五个主要挑战:

  1. 巨大的数据集和高的摄取率。许多数据驱动的应用在网页分析、金融和电子商务等行业上都有巨大的并持续增长的数据量。为处理巨大的数据集,分析数据库不仅必须提供有效的索引和压缩策略,还需支持数据在多个节点上的分布(横向扩展),因为单台服务器的存储容量通常限制在几十TB。此外,最近的数据通常对实时洞察的相关性更大。因此,分析数据库必须能够以稳定的高速率或突发的方式摄取新数据,并在不延迟的情况下,持续“降低优先级”(例如聚合、归档)历史数据,而不减缓并行报告查询的速度。

  2. 许多同时查询以及对低延迟的期望。查询通常可以分类为临时的(例如探索性数据分析)或定期的(例如周期性仪表板查询)。用例越交互,期望的查询延迟就越低,因此在查询优化和执行上面临挑战。定期查询还提供了根据工作负载调整物理数据库布局的机会。因此,数据库应提供修剪技术,以优化频繁的查询。视查询的优先级,数据库必须同时提供相等或优先级的共享系统资源访问(如 CPU、内存、磁盘和网络 I/O),即便有大量查询同时运行。

  3. 多样化的数据存储、存储位置和格式。为了与现有数据架构整合,现代分析数据库应展示出高度的开放性,能够在任何系统、位置或格式下读写外部数据。

  4. 方便的查询语言,支持性能检测。OLAP 数据库的现实使用提出了额外的“软性”要求。例如,用户通常喜欢用具表现力的 SQL 方言与数据库接口,这种方言能够支持嵌套数据类型和广泛的常规、聚合和窗口函数。分析数据库还应提供复杂的工具,以便检测系统或单个查询的性能。

  5. 行业级的稳健性和多样化的部署。由于普通硬件不可靠,数据库必须提供数据复制,以应对节点故障所带来的风险。此外,数据库应在任何硬件上运行,从旧笔记本到强大的服务器。最后,为了避免 JVM 程序中垃圾收集的开销并支持裸金属性能(例如 SIMD),数据库理想上应作为目标平台的本机二进制文件进行部署。

图 1: ClickHouse 时间线。

2 架构

图 2: ClickHouse 数据库引擎的高层架构。

图 2所示,ClickHouse 引擎分为三个主要层次:查询处理层(在第 4 节中描述)、存储层(第 3 节)和集成层(第 5 节)。此外,还提供了一个访问层,用于管理用户会话与应用之间通过不同协议的通信。还有正交组件用于线程、缓存、基于角色的访问控制、备份和持续监控。ClickHouse 使用 C++ 构建,作为一个单一的、静态链接的二进制文件,无依赖。

查询处理遵循传统范式,包括解析输入查询、构建并优化逻辑和物理查询计划以及执行。ClickHouse 使用类似于 MonetDB/X100 的向量化执行模型 [11],并结合机会主义代码编译 [53]。查询可以使用功能丰富的 SQL 方言、PRQL [76] 或 Kusto 的 KQL [50] 编写。

存储层由不同的表引擎组成,这些引擎封装了表数据的格式和位置。表引擎分为三类:第一类是 MergeTree* 表引擎家族,它们代表 ClickHouse 中的主要持久化格式。基于 LSM 树的概念 [60],表按水平划分成有序的部分,并由后台进程持续合并。各个 MergeTree* 表引擎在合并输入部分的行的方式上有所不同。例如,如果行过时,可以对其进行聚合或替换。

第二类是专用表引擎,用于加速或分布查询执行。这类引擎包括称为字典的内存中键值表引擎。一个字典定期缓存针对内部或外部数据源执行的查询结果。这大大减少了在数据陈旧的情况下的访问延迟。其他专用表引擎的示例包括用于临时表的纯内存引擎以及用于透明数据分片的分布式表引擎(见下文)。

第三类表引擎是用于与外部系统(如关系数据库即 PostgreSQL、MySQL,发布/订阅系统即 Kafka、RabbitMQ [24],或键/值存储即 Redis)进行双向数据交换的虚拟表引擎。虚拟引擎还可以与数据湖(如 Iceberg、DeltaLake、Hudi [36])或对象存储中的文件(如 AWS S3、Google GCP)进行交互。

ClickHouse 支持在多个集群节点上对表进行分片和复制,以实现可扩展性和可用性。分片根据分片表达式将表划分为一组表分片。各个分片是相互独立的表,通常位于不同节点上。客户可以直接读取和写入分片,即将其视为单独的表,或使用分布式专用表引擎,该引擎提供所有表分片的全局视图。分片的主要目的是处理超出单个节点容量的数据集(通常为几十 TB 的数据)。分片的另一个用途是将表的读写负载分布到多个节点,即负载均衡。与此正交的是,分片可以在多个节点之间复制,以容忍节点故障。为此,每个 Merge-Tree* 表引擎有相应的 ReplicatedMergeTree* 引擎,该引擎使用基于 Raft 一致性 [59] 的多主协调机制(由Keeper实现,这是一个用 C++ 编写的Apache Zookeeper的替代品),以保证每个分片在任何时候都有可配置数量的副本。第 3.6 节详细讨论了复制机制。举个例子,图 2 显示了一个带有两个分片的表,每个分片复制到两个节点上。

最后,ClickHouse 数据库引擎可以以本地、云、独立或进程内模式运行。在本地模式下,用户在本地将 ClickHouse 设置为单个服务器或具有分片和/或复制的多节点集群。客户端通过本机、MySQL 或 PostgreSQL 的二进制协议,或 HTTP REST API 与数据库通信。云模式由 ClickHouse Cloud 表示,这是一个完全托管的自动扩展 DBaaS 产品。虽然本文重点介绍本地模式,但我们计划在后续出版物中描述 ClickHouse Cloud 的架构。独立模式将 ClickHouse 变成一个命令行工具,用于分析和转换文件,使其成为 SQL 为基础的 Unix 工具(如 cat 和 grep)的替代品。虽然这无需任何事先配置,但独立模式仅限于单个服务器。最近,开发了一种称为 chDB 的进程内模式 [15],适用于像 Jupyter notebooks [37] 与 Pandas 数据框这样的交互式数据分析用例。受到 DuckDB [67] 的启发,chDB 将 ClickHouse 嵌入为高性能 OLAP 引擎到宿主进程中。与其他模式相比,这种模式允许在数据库引擎与应用程序之间有效地传递源和结果数据,而无须进行复制,因为它们在相同的地址空间中运行。

3 存储层

本节讨论 MergeTree* 表引擎作为 ClickHouse 的原生存储格式。我们描述它们的磁盘表示,并讨论 ClickHouse 中的三种数据修剪技术。随后,我们呈现合并策略,这些策略在不影响并发插入的情况下不断转化数据。最后,我们解释如何实现更新与删除,以及数据去重、数据复制和 ACID 合规性。

3.1 磁盘格式

每个在 MergeTree* 表引擎中的表都组织为不可变的表部分集合。每当一组行插入到表中时,就会创建一个部分。部分是自包含的,这意味着它们包括了解释其内容所需的所有元数据,而无需额外查找中心目录。为保持每个表的部分数量较低,后台合并作业会定期将多个较小的部分合并为一个更大的部分,直到达到可配置的部分大小(默认情况下为150 GB)。由于部分是按表的主键列排序的(详见第 3.2 节),合并时使用有效的 k-way 合并排序 [40]。源部分在其引用计数降为零之后将被标记为非活动部分,并最终删除,即不再有查询从这些部分读取数据。

行可以在两种模式下插入:在同步插入模式下,每个INSERT语句创建一个新部分,并将其附加到表中。为了减少合并的开销,数据库客户端被鼓励批量插入元组,例如一次插入20,000行。然而,如果数据需要实时分析,客户端批处理造成的延迟往往是不可接受的。例如,监控用例通常涉及数千个监控代理不断发送少量事件和指标数据。这种场景可利用异步插入模式,其中 ClickHouse 从多个传入的INSERT批处理中缓冲行到同一表中,只有当缓冲区大小超过可配置阈值或超时到期后才创建新部分。

图 3: MergeTree*-引擎表的插入与合并。

图 3说明了对 MergeTree*-引擎表的四次同步和两次异步插入。两次合并将活跃部分的数量从最初的五个减少到两个。

与 LSM 树 [58] 及其在各种数据库中的实现 [13, 26, 56] 相比,ClickHouse 将所有部分视为相等,而不是将它们按层次结构排列。因此,合并不再限于同一层级的部分。由于这一点也放弃了部分的隐式时间顺序,因此需要基于非 Tombstone 的更新和删除的替代机制(见第 3.4 节)。ClickHouse 直接将插入写入磁盘,而其他基于 LSM 的存储通常使用预写日志(见第 3.7 节)。

一个部分对应磁盘上的一个目录,其中包含每个列的一个文件。作为一种优化,小部分(默认小于10 MB)的列顺序存储在一个文件中,以增加读取和写入的空间局部性。一个部分的行进一步在逻辑上被划分为8192条记录的组,称为 granules。一个 granule 代表 ClickHouse 中扫描和索引查找操作处理的最小不可分割的数据单元。然而,磁盘数据的读取和写入并不是在 granule 级别上执行,而是以块的粒度进行,这些块结合了一个列内多个相邻的 granules。新块的形成基于每块可配置的字节大小(默认1 MB),即块内的 granules 数量是可变的,取决于列的数据类型和分布。块还会进一步压缩以减少其大小和 I/O 成本。默认情况下,ClickHouse 使用 LZ4 [75] 作为通用压缩算法,但用户也可以指定像 Gorilla [63] 或 FPC [12] 这样的专业编码器,尤其用于浮点数据。压缩算法也可以拼接。例如,可以先使用增量编码 [23] 减少数字值中的逻辑冗余,然后执行重量级压缩,最后使用 AES 编码器对数据进行加密。在块从磁盘加载到内存时,块会在不经过解压的情况下解压缩。为了在压缩的情况下,实现对单个 granules 的快速随机访问,ClickHouse 额外存储了每列的映射,将每个 granule 的 ID 与其所在压缩块在列文件中的偏移量以及该 granule 在未压缩块中的偏移量关联起来。

列还可以使用两个特殊的包装数据类型进行字典编码 [2, 77, 81] 或可为空:LowCardinality(T)通过整数 ID 替换原始列值,从而显著减少具有少量唯一值的数据的存储开销。Nullable(T) 为列 T 添加一个内部位图,表示列值是否为 NULL。

最后,表可以使用任意分区表达式进行范围、哈希或轮询式分区。为了启用分区修剪,ClickHouse 额外存储每个分区的分区表达式的最小和最大值。用户还可以选择创建更高级的列统计信息(例如,HyperLogLog [30] 或 t-digest [28] 统计),也提供基数估计。

3.2 数据修剪

在大多数使用场景中,仅仅扫描 PB 级的数据来回答一个查询是太慢且成本昂贵的。 ClickHouse 支持三种数据修剪技术,可以在搜索过程中跳过大多数行,从而显著加快查询速度。

首先,用户可以为表定义主键索引。主键列决定了每个部分内行的排序顺序,即该索引是局部集群的。ClickHouse 还为每个部分存储了一个映射,从每个 granule 的第一行主键列值到该 granule 的 ID,即该索引是稀疏的 [31]。生成的数据结构通常足够小,可以完全存放在内存中,例如,仅需要1000条条目就可以对810万行建立索引。主键的主要目的是使用二进制搜索而不是顺序扫描来评估经常过滤的列的等值和范围谓词(见第 4.4 节)。局部排序还可用于部分合并和查询优化,例如基于排序的聚合或在主键列构成排序列的前缀时,从物理执行计划中删除排序运算符。

图 4 显示了一个为页面展示统计信息的表上的事件时间列的主键索引。可以通过对主键索引进行二叉搜索找到与查询中的范围谓词匹配的 granules,而无需顺序扫描事件时间。

图 4: 用主键索引评估过滤器。

其次,用户可以创建表投影,即包含相同行但按不同主键排序的表的替代版本 [71]。投影允许加速对与主表的主键不同的列进行过滤的查询,但代价是插入、合并和空间消耗带来的开销增加。默认情况下,投影仅从新插入主表的部分懒惰填充,而不是从现有部分中填充,除非用户完全具现化该投影。查询优化器基于估算的 I/O 成本选择从主表或投影中读取。如果某部分不存在投影,则查询执行会回退到相应的主表部分。

第三,跳过索引提供了一种轻量级替代方案,替代投影。跳过索引的想法是,在多个连续 granules 的级别上存储少量元数据,从而避免扫描不相关的行。可以为任意索引表达式创建跳过索引,并使用可配置的粒度,即跳过索引块中的 granules 数量。可用的跳过索引类型包括:1. 最小-最大索引 [51],存储每个索引块的索引表达式的最小值和最大值。此索引类型适用于具有小绝对范围的局部聚集数据,例如松散排序的数据。 2. 集合索引,存储可配置数量的唯一索引块值。在具有小局部基数的数据中(即“聚集在一起”的值)时,这些索引效果最佳。 3. Bloom 过滤器索引 [9],为行、标记或 n-gram 值构建,具有可配置的误报率。这些索引支持文本搜索 [73],但与最小-最大和集合索引不同,它们不能用于范围或非谓词。

3.3 合并时数据转化

商业智能和可观察性用例通常需要处理以高频或突发方式生成的数据。此外,最近生成的数据通常在有意义的实时洞察中比历史数据更相关。这类用例要求数据库在持续以高数据摄取率的同时,通过聚合或数据老化等技术不断减少历史数据的量。 ClickHouse 允许使用不同的合并策略持续增量转化现有数据。合并时数据转化不会影响 INSERT 语句的性能,但无法保证表中永远不会包含不需要的(例如过时或非聚合的)值。如有必要,可以通过在 SELECT 语句中指定关键字 FINAL 在查询时应用所有合并时转化。

替换合并仅保留基于其包含部分的创建时间戳的元组最近插入的版本,旧版本被删除。如果元组的主键列值相同,则被认为是等效的。为了明确控制保留哪个元组,也可以指定一个特殊版本列进行比较。替换合并通常用作合并时更新机制(通常用于更新频繁的用例)或作为插入时数据去重的替代方案(见第 3.5 节)。

聚合合并将具有相同主键列值的行合并为一个聚合行。非主键列必须处于部分聚合状态,以保持汇总值。两个部分聚合状态,例如用于 avg() 的和与计数,将合并为一个新的部分聚合状态。聚合合并通常用于物化视图,而不是普通表。物化视图是基于对源表的转化查询进行填充的。与其他数据库不同,ClickHouse 不定期使用源表的全部内容刷新物化视图。物化视图更是随着新部分插入源表时对转化查询的结果进行增量更新。

图 5 显示了基于页面展示统计信息的表定义的物化视图。对于新插入源表的部分,转化查询计算按区域分组的平均延迟和最大延迟,并将结果插入物化视图。聚合函数 avg() 和 max() 及扩展 -State 返回部分聚合状态,而不是实际结果。为物化视图定义的聚合合并持续结合不同部分中的部分聚合状态。为了获得最终结果,用户使用 avg() 和 max() 的 -Merge 扩展在物化视图中整合部分聚合状态。

图 5: 物化视图中的聚合合并。

TTL (生存时间) 合并为历史数据提供老化。与删除和聚合合并不同,TTL 合并每次仅处理一个部分。TTL 合并是通过触发器和动作的规则来定义的。触发器是对每一行计算时间戳的表达式,该时间戳与 TTL 合并运行时的时间进行比较。虽然这使用户能够在行粒度上控制动作,我们发现检查所有行是否满足给定条件并在整个部分上运行动作是足够的。可用的动作包括 1. 将部分移动到另一个卷(例如,更便宜且较慢的存储),2. 重新压缩该部分(例如使用更重量级的编码器),3. 删除该部分,以及 4. 汇总,即使用分组键和聚合函数对行进行聚合。

作为例子,考虑列表 1中的日志表定义。 ClickHouse 将把时间戳列值早于一周的部分移动到慢速但便宜的 S3 对象存储中。

列表 1: 将部分在一周后移动到对象存储中。

3.4 更新和删除

MergeTree* 表引擎的设计更倾向于追加工作负载,但某些用例需要偶尔修改现有数据,例如为合规性提供服务。更新或删除数据有两种方法,均不会阻塞并行插入。

变更以就地方式重写表的所有部分。为了防止表(删除)或列(更新)暂时双倍增加大小,此操作是非原子的,即并行的SELECT语句可能会读取变更和非变更的部分。变更保证在操作结束时数据物理上被更改。删除变更仍然代价高昂,因为它们重写所有部分中的所有列。

作为替代方案,轻量级删除仅更新内部位图列,指示某行是否被删除。ClickHouse 会在 SELECT 查询中添加对位图列的额外过滤器,以将被删除的行从结果中排除。被删除的行仅在未来的某个未指定时间通过常规合并物理删除。根据列的数量,轻量级删除比变更可能快得多,但代价是 SELECT 查询的速度会更慢。

在同一表上执行更新和删除操作预计是罕见且需要串行化的,以避免逻辑冲突。

3.5 幂等插入

在实践中,客户在向服务器发送数据以插入表后如何处理连接超时的问题是一个常见问题。在这种情况下,客户很难区分数据是成功插入还是没有。这个问题通常通过从客户端重新发送数据到服务器并依赖主键或唯一约束来拒绝重复插入来解决。数据库通过基于二叉树 [39, 68] 、基数树 [45] 或哈希表 [29] 的索引结构快速执行所需的查找。由于这些数据结构对每个元组进行索引,因此在大数据集和高摄取率的情况下,它们的空间和更新开销变得不堪重负。

ClickHouse 提供了一种更轻量级的替代方案,基于每个插入最终都会创建一个部分的事实。更具体地说,服务器维护最近插入的 N 个部分的哈希(例如,N=100),并忽略已知哈希的部分的重新插入。对于非复制和复制表,哈希分别存储在本地或 Keeper 中。因此,插入变得是幂等的,即客户端可以在超时后简单地重新发送相同的行批,并假定服务器处理去重。对于对去重过程有更高控制要求,客户端可以选择提供一个插入令牌,作为部分哈希。在基于哈希的去重过程中,会产生与哈希新行相关的开销,但存储和比较哈希的成本可以忽略不计。

3.6 数据复制

复制是高可用性(对节点故障的容忍)的前提条件,同时也用于负载均衡和零停机升级 [14]。在 ClickHouse 中,复制是基于表状态的概念,这些状态由一组表分片(第 3.1 节)和表元数据(如列名和类型)组成。节点通过三种操作来推进表的状态:1. 插入操作向状态中添加一个新分片,2. 合并操作向状态中添加新分片并删除现有分片,3. 变更和 DDL 语句根据具体操作添加分片、删除分片和/或更改表元数据。操作在单个节点上本地执行,并作为全局复制日志中的一系列状态转换记录。

复制日志由通常三个 ClickHouse Keeper 进程的集群维护,这些进程使用 Raft 共识算法 [59] 提供一个用于 ClickHouse 节点集群的分布式和容错协调层。所有集群节点最初指向复制日志中的同一位置。当节点执行本地插入、合并、变更和 DDL 语句时,复制日志会在所有其他节点上异步重放。因此,复制表仅最终一致,即节点可以暂时读取较旧的表状态,同时朝着最新状态汇聚。上述大多数操作也可以选择在达到节点法定人数(例如大多数节点或所有节点)采用新状态之前同步执行。

举个例子,图 6 显示了一个在三个 ClickHouse 节点集群中最初为空的复制表。节点 1 首先接收到两个插入语句并将其记录在存储在 Keeper 集合中的复制日志中(1 2)。接着,节点 2 通过提取第一个日志条目重放它(3)并从节点 1 下载新分片(4),而节点 3 重放两个日志条目(3 4 5 6)。最后,节点 3 将两个分片合并为一个新分片,删除输入分片,并在复制日志中记录合并条目(7)。

图 6:三节点集群中的复制。

为加快同步,有三种优化措施:首先,新加入集群的节点不会重新播放复制日志,而是简单地复制写入最后一个复制日志条目的节点的状态。其次,合并通过在本地重复它们或从其他节点提取结果分片来重放。具体的行为是可配置的,能够平衡 CPU 消耗和网络 I/O。例如,跨数据中心的复制通常更倾向于本地合并,以降低运营成本。第三,节点并行重放彼此独立的复制日志条目。例如,连续插入到同一表中的新分片的提取,或不同表上的操作。

3.7 ACID 合规性

为了最大化并发读写操作的性能, ClickHouse 尽可能避免加锁。查询是在查询开始时创建的所有相关表中所有分片的快照上执行的。这确保了由并行 INSERT 或合并(第 3.1 节)插入的新分片不参与执行。为防止分片被同时修改或删除(第 3.4 节),在查询执行期间处理的分片的引用计数会增加。形式上,这对应于基于版本分片的 MVCC 变体实现的快照隔离 [6]。因此,除非在快照拍摄时并发写入每个只影响一个分片的偶然情况,语句通常不符合 ACID 标准。

实际上,大多数 ClickHouse 的重写决策使用案例甚至可以容忍由于电源故障而导致的新数据丢失的风险。数据库利用这一点,默认情况下不强制将新插入的分片提交(fsync)到磁盘,从而允许内核以牺牲原子性的代价批量写入。

4 查询处理层

图 7:跨 SIMD 单元、核心和节点的并行化。

图 7 所示,ClickHouse 在数据元素、数据块和表分片的级别上并行化查询。多个数据元素可以使用 SIMD 指令在操作符中同时处理。在单个节点上,查询引擎在多个线程中同时执行操作符。 ClickHouse 使用与 MonetDB/X100 相同的向量化模型 [11],即操作符生成、传递和消费多个行(数据块),而不是单行,以最小化虚拟函数调用的开销。如果源表被拆分为不相交的表分片,则多个节点可以同时扫描这些分片。因此,所有硬件资源都得到了充分利用,查询处理可以通过添加节点横向扩展,通过添加核心纵向扩展。

本节的其余部分首先更详细地描述了在数据元素、数据块和分片粒度下的并行处理。然后我们介绍一些关键优化,以最大化查询性能。最后,我们讨论 ClickHouse 如何在并发查询存在的情况下管理共享系统资源。

4.1 SIMD 并行化

在操作符之间传递多行为向量化创造了机会。向量化要么基于手动编写的内联函数 [64, 80],要么基于编译器的自动向量化 [25]。受益于向量化的代码被编译成不同的计算内核。例如,查询操作符的内部热循环可以根据非向量化内核、自动向量化的 AVX2 内核以及手动向量化的 AVX-512 内核实现。运行时动态选择最快的内核 选择依据 是 cpuid 指令。这种方法使 ClickHouse 能够在最老的系统(需要 SSE 4.2 作为最低要求)上运行,同时在最近的硬件上提供显著的加速。

4.2 多核并行化

图 8:具有三条通道的物理操作符计划。

ClickHouse 遵循传统方法 [31] 将 SQL 查询转换为物理计划操作符的有向图。操作符计划的输入由特殊源操作符表示,这些操作符以原生格式或支持的任何第三方格式读取数据(见第 5 节)。同样,特殊的接收操作符将结果转换为所需的输出格式。在查询编译期间,物理操作符计划被展开为基于可配置最大工作线程数(默认是核心数量)和源表大小的独立执行通道。通道将由并行操作符处理的数据分解为不重叠的范围。为了最大化并行处理的机会,通道合并的时间尽可能推迟。

例如,图 8 中节点 1 的框显示了针对具有页面展示统计的表的典型 OLAP 查询的操作符图。在第一阶段,源表的三个不相交范围同时被过滤。重新分区交换操作符动态路由结果块在第一和第二阶段之间,以保持处理线程的均匀利用。如果扫描的范围具有显著不同的选择性,第一阶段之后通道可能会变得不平衡。在第二阶段,对过滤器存活下来的行按 RegionID 分组。聚合操作符维护以 RegionID 作为分组列的本地结果组,并以每组的总和和计数作为 avg() 的部分聚合状态。本地聚合结果最终通过 GroupStateMerge 操作符合并为全局聚合结果。这个操作符也是一个管道中断器,即第三阶段只能在聚合结果完全计算后才开始。在第三阶段,结果组首先通过分发交换操作符划分为三个相等大小的不重叠分区,然后按 AvgLatency 排序。排序分为三个步骤:首先,ChunkSort 操作符对每个分区的个别块进行排序。其次,StreamSort 操作符保持本地排序结果,将其与传入的排序块结合使用 2 路归并排序。最后,MergeSort 操作符通过 k-way 排序组合本地结果以获得最终结果。

操作符是状态机,并通过输入和输出端口相互连接。操作符的三种可能状态是需要块、准备就绪和完成。要从需要块转换到准备就绪,必须将一个块放入操作符的输入端口。要从准备就绪转换到完成,操作符处理输入块并生成输出块。要从完成转换到需要块,输出块从操作符的输出端口中移除。连接的两个操作符中的第一次和第三次状态转换只能在组合步骤中执行。源操作符(接收操作符)只有准备就绪和完成(需要块和完成)两种状态。

工作线程持续遍历物理操作符计划并执行状态转换。为了保持 CPU 缓存热,计划包含提示,要求同一线程在同一通道中处理连续的操作符。在阶段内,平行处理同时发生在不相交的输入上(例如在 图 8 中,聚合操作符同时执行),并且在没有被管道中断器分隔的阶段垂直发生(例如在 图 8 中,处于同一通道的筛选和聚合操作符可以同时运行)。为了避免新查询启动或并发查询结束时发生超额或不足订阅,查询期间可以在一个查询和最大工作线程数量之间改变并行度(见第 4.5 节)。

操作符还可以以两种方式在运行时进一步影响查询执行。首先,操作符可以动态创建和连接新操作符。主要用于在内存消耗超过可配置阈值时,切换到外部聚合、排序或连接算法,而不是取消查询。其次,操作符可以请求工作线程移动到异步队列。这使得在等待远程数据时更有效地使用工作线程。

ClickHouse 的查询执行引擎与以小块驱动的并行性 [44] 类似,因为通道通常在不同的核心/NUMA 插座上执行,并且工作线程可以从其他通道窃取任务。此外,没有中央调度组件;相反,工作线程通过持续遍历操作符计划来单独选择其任务。与以小块驱动的并行性不同, ClickHouse 将最大并行度内置于计划中,使用与可用的小块大小相比更大的范围来分区源表。虽然这在某些情况下可能导致停滞(例如,当不同通道中的筛选器操作符的运行时差异很大时),但我们发现自由使用像重新分区这样的交换操作符至少可以避免这种不平衡在阶段之间的积累。

4.3 多节点并行化

如果查询的源表已分片,接收查询的节点(发起节点)会尝试在其他节点上尽可能多地执行工作。来自其他节点的结果可以集成到查询计划中的不同点。根据查询,远程节点可以:1. 将原始源表列流传输到发起节点,2. 过滤源列并发送存活的行,3. 执行过滤和聚合步骤并发送局部结果组,带有部分聚合状态,或 4. 运行整个查询,包括过滤、聚合和排序。

图 8 中的节点 2 ... N 显示在持有命中表分片的其他节点上执行的计划片段。这些节点过滤和分组本地数据,并将结果发送到发起节点。在节点 1 上的 GroupStateMerge 操作符在结果组最终排序之前合并本地和远程结果。

4.4 整体性能优化

本节介绍应用于查询执行不同阶段的关键性能优化。

查询优化。第一组优化应用于根据查询的 AST 获得的语义查询表示。此类优化的示例包括常量折叠(例如,concat(lower('a'),upper('b')) 变为 'aB')、从某些聚合函数中提取标量(例如,sum(a2) 变为 2 * sum(a))、消除公共子表达式,以及将等式过滤器的析取转换为 IN 列表(例如,x=c 或 x=d 变为 x IN (c,d))。优化后的语义查询表示随后转换为逻辑操作符计划。对逻辑计划的优化包括过滤向下推送,根据估算的较高开销重新排序函数评估和排序步骤。最后,逻辑查询计划被转换为物理操作符计划。此转换可以利用所涉及表引擎的特殊性。例如,在 MergeTree-表引擎的情况下,如果 ORDER BY 列构成主键的前缀,则可以按磁盘顺序读取数据,并且可以从计划中删除排序操作符。此外,如果聚合中的分组列形成主键的前缀, ClickHouse 可以使用排序聚合 [33],即直接在预排序输入中聚合相同值的运行。相比于哈希聚合,排序聚合显著减少了内存消耗,并且聚合值可以在处理运行后立即传递给下一个操作符。

查询编译。 ClickHouse 采用 基于 LLVM 的查询编译 动态融合相邻的计划操作符 [38, 53]。例如,表达式 a * b + c + 1 可以组合成一个操作符,而不是三个操作符。除了表达式, ClickHouse 还使用编译来一次评估多个聚合函数(即用于 GROUP BY)以及用于具有多个排序键的排序。查询编译减少了虚拟调用的数量,保持数据在寄存器或 CPU 缓存中,并帮助分支预测,因为需要执行的代码减少。此外,运行时编译启用了一系列丰富的优化,例如编译器实现的逻辑优化和观察窗优化,并提供对可用最快 CPU 指令的访问。编译仅在相同的常规、聚合或排序表达式由不同查询执行超过可配置次数时启动。编译的查询操作符被缓存,并可以被未来的查询重用。[7]

主键索引评估。如果条件中的过滤子句的合取范式中的一个子集构成主键列的前缀, ClickHouse 将使用主键索引评估 WHERE 条件。主键索引从按字母顺序排序的键值范围左到右进行分析。对应于主键列的过滤子句采用三元逻辑进行评估——它们对范围内的值都是正确的、都是错误的,或混合的真/假。在后者情况下,范围被拆分为递归分析的子范围。对于过滤条件中的函数还存在额外的优化。首先,函数具有描述其单调性的特征,例如,toDayOfMonth(date) 在一个月内是分段单调的。单调性特征允许推断函数在已排序输入键值范围内生成的结果是否有序。其次,一些函数可以计算给定函数结果的原像。这用于用函数调用替换常量的比较,通过比较键列值与原像。例如,toYear(k) = 2024 可以替换为 k >= 2024-01-01 && k < 2025-01-01。

数据跳过。 ClickHouse 尝试在查询运行时通过使用第 3.2 节 中介绍的数据结构来避免读取数据。此外,基于启发式和(可选)列统计信息,相较于估算选择性,针对不同列的过滤器按降序逐个评估。仅传递至少包含一行匹配结果的数据块到下一个谓词。这逐渐减少了从谓词到谓词应读取的数据量和需要执行的计算量。此优化仅在至少存在一个高选择性谓词时应用;否则,查询的延迟会相较于并行评估所有谓词而恶化。

哈希表。哈希表是聚合和哈希连接的基本数据结构。选择合适的哈希表类型对性能至关重要。 ClickHouse 实例化 多种哈希表(截至 2024 年 3 月超过 30 种)来自通用哈希表模板,变体点包括哈希函数、分配器、单元类型和调整策略。根据分组列的数据类型、估算的哈希表基数及其他因素,为每个查询操作符选择最快的哈希表。哈希表还实现了更进一步的优化,包括:

  • 带有 256 个子表的两级布局(基于哈希的第一个字节),以支持巨大的键集,
  • 具有四个子表和不同哈希函数的字符串哈希表 [79],适用于不同字符串长度,
  • 当键数很少时直接将键用作桶索引的查找表(即不哈希),
  • 嵌入哈希的值,用于在比较开销较高时实现更快的冲突解决(例如,字符串、AST),
  • 根据运行时统计信息预测大小来创建哈希表,以避免不必要的调整,
  • 在单个内存块上分配多个具有相同创建/销毁生命周期的小哈希表,
  • 使用每个哈希映射和每个单元版本计数器即时清除哈希表以供重用,
  • 使用 CPU 预取(__builtin_prefetch)加速在哈希键之后检索值的过程。

连接。由于 ClickHouse 最初仅初步支持连接,许多用例在历史上求助于非规范化表。如今,数据库 提供 SQL 中所有可用的连接类型(内连接、左/右/完全外连接、交叉连接、基于时间的连接),以及不同的连接算法,如哈希连接(简单、优雅)、排序-合并连接和对具有快速键值查找的表引擎(通常是字典)的索引连接。

由于连接是数据库中最昂贵的操作之一,提供经典连接算法的并行变体至关重要,理想情况下带有可配置的空间/时间权衡。对于哈希连接, ClickHouse 实现了 [7] 的无阻塞共享分区算法。例如,图 9 中的查询通过对页面命中统计表进行自连接计算用户在 URL 之间的移动。连接的构建阶段被拆分为三个通道,覆盖源表的三个不相交范围。使用分区哈希表而不是全局哈希表。工作线程通常确定构建侧每个输入行的目标分区,方法是计算哈希函数的模数。访问哈希表分区的同步使用 Gather 交换操作符。探测阶段类似地找到其输入元组的目标分区。虽然此算法为每个元组引入了两个额外的哈希计算,但它大大减少了构建阶段的锁争用,具体取决于哈希表分区的数量。

图 9:具有三个哈希表分区的并行哈希连接。

4.5 工作负载隔离

ClickHouse 提供并发控制、内存使用限制和 I/O 调度,使用户能够将查询隔离到工作负载类中。通过为特定工作负载类设置共享资源(CPU 核心、DRAM、磁盘和网络 I/O)的限制,确保这些查询不会影响其他关键业务查询。

并发控制防止在高并发查询的场景中线程超额分配。更具体地说,每个查询的工作线程数量根据指定的与可用 CPU 核心数量的比例动态进行调整。

ClickHouse 在服务器、用户和查询级别跟踪内存分配的字节大小,从而允许设置灵活的内存使用限制。内存过量承诺使查询能够使用超出保证内存的额外空闲内存,同时保障其他查询的内存限制。此外,聚合、排序和连接子句的内存使用可以被限制,导致当内存限制被超出时回退到外部算法。

最后, I/O 调度允许用户根据最大带宽、进程中的请求和策略(例如 FIFO、SFC [32])限制工作负载类的本地和远程磁盘访问。

5 集成层

实时决策应用程序通常依赖于高效和低延迟的多地点数据访问。使外部数据在 OLAP 数据库中可用有两种方法。使用基于推送的数据访问,第三方组件将在数据库与外部数据存储之间架起桥梁。比如,专业的提取-转换-加载 (ETL) 工具会将远程数据推送到目标系统。在基于拉取的模型中,数据库本身连接到远程数据源并将数据拉取到本地表进行查询,或将数据导出到远程系统。尽管基于推送的方法更为灵活和普遍,但它们造成更大的架构占用和可扩展性瓶颈。相比之下,数据库中的远程连接提供了有趣的能力,例如本地和远程数据之间的连接,同时保持整体架构简单,减少对洞察的获取时间。

本节其余部分探讨 ClickHouse 中基于拉取的数据集成方法,旨在访问远程位置的数据。我们指出, SQL 数据库中的远程连接理念并不新颖。例如, SQL/MED 标准 [35],于 2001 年引入,自 2011 年以来已由 PostgreSQL 实现 [65],提出了外部数据包装器作为管理外部数据的统一接口。ClickHouse 的设计目标之一是与其他数据存储和存储格式实现最佳互操作性。截至 2024 年 3 月, ClickHouse 在所有分析数据库中提供了我们所知的最多内置数据集成选项。

外部连接。 ClickHouse 提供 50+ 个集成表函数和引擎,以实现与外部系统和存储位置的连接,包括 ODBC、MySQL、PostgreSQL、SQLite、Kafka、Hive、MongoDB、Redis、S3/GCP/Azure 对象存储和各种数据湖。我们将其进一步划分为以下图表所示的类别(不属于原始 vldb 论文的一部分)。

附录图:ClickBench 的互操作性选项。

临时访问与集成 表函数。表函数可以在 SELECT 查询的 FROM 子句中调用,以读取远程数据进行探索性临时查询。或者,使用 INSERT INTO TABLE FUNCTION 语句将数据写入远程存储。

持续访问。创建与远程数据存储和处理系统的永久连接有三种方法。

首先,集成 表引擎 将远程数据源(例如 MySQL 表)表示为持久的本地表。用户使用 CREATE TABLE AS 语法结合 SELECT 查询和表函数来存储表定义。用户可以指定自定义架构,例如,仅引用远程列的子集,或使用架构推断自动确定列名和等效的 ClickHouse 类型。我们进一步区分被动和主动运行时行为:被动表引擎将查询转发到远程系统,并用结果填充本地代理表。相比之下,主动表引擎定期从远程系统拉取数据或订阅远程更改,例如通过 PostgreSQL 的逻辑复制协议。因此,本地表包含远程表的完整副本。

其次,集成 数据库引擎 将远程数据存储中表架构的所有表映射到 ClickHouse。与前者不同,它们通常要求远程数据存储是关系数据库,并且另外提供对 DDL 语句的有限支持。

第三,字典 可以使用针对几乎所有可能的数据源的任意查询,与相应的集成表函数或引擎进行填充。运行时行为是主动的,因为数据在固定间隔内从远程存储中拉取。

数据格式。为了与第三方系统进行交互,现代分析数据库还必须能够处理任何格式的数据。除了其原生格式外, ClickHouse 还支持 90+ 种格式,包括 CSV、JSON、Parquet、Avro、ORC、Arrow 和 Protobuf。每种格式可以作为输入格式(ClickHouse 可以读取)、输出格式(ClickHouse 可以导出)或两者。有些面向分析的格式如 Parquet 还与查询处理集成,即优化器可以利用嵌入统计信息,并且在压缩数据上直接评估过滤器。

兼容性接口。除了其原生二进制通信协议和 HTTP,客户端还可以通过与 MySQL 或 PostgreSQL 通信协议兼容的接口与 ClickHouse 交互。此兼容性功能有助于从专有应用程序(例如某些商业智能工具)访问,因为这些供应商尚未实现原生 ClickHouse 连接。

6 性能作为一种特性

本节介绍用于性能分析的内置工具,并根据真实世界和基准查询评估性能。

6.1 内置性能分析工具

提供广泛的工具以调查单个查询或后台操作中的性能瓶颈。用户通过基于系统表的统一接口与所有工具交互。

服务器和查询指标。服务器级统计信息,如活动分片数量、网络吞吐量和缓存命中率,补充了按查询的统计信息,如读取的块数或索引使用统计。指标在请求时同步计算,或在可配置的时间间隔内异步计算。

采样分析器。可以使用采样分析器收集服务器线程的调用堆栈。结果可以选择性地导出到外部工具,如火焰图可视化工具。

OpenTelemetry 集成。OpenTelemetry 是一个用于跨多个数据处理系统跟踪数据行的开放标准 [8]。 ClickHouse 可以为所有查询处理步骤生成 OpenTelemetry 日志跨度,具有可配置的粒度,以及收集和分析来自其他系统的 OpenTelemetry 日志跨度。

解释查询。与其他数据库一样, SELECT 查询可以通过 EXPLAIN 进行详细分析,提供对查询的 AST、逻辑和物理操作符计划及其执行时行为的深入了解。

6.2 基准测试

尽管基准测试因不够现实而受到批评 [10, 52, 66, 74],它仍然有助于识别数据库的优缺点。接下来,我们讨论如何使用基准测试来评估 ClickHouse 的性能。

6.2.1 非规范化表

对非规范化事实表的过滤和聚合查询历来是 ClickHouse 的主要使用案例。我们报告了 ClickBench 的运行时间,这是一个典型的工作负载,模拟了用于点击流和流量分析的临时及定期报告查询。基准测试包含对包含 1 亿条匿名页面访问记录的表进行的 43 个查询,这些记录来自网络上最大的分析平台之一。一个在线仪表板 [17] 显示了截至 2024 年 6 月对 45 个商业和研究数据库的测量(冷/热运行时间、数据导入时间、磁盘大小)。结果由独立贡献者根据公开可用的数据集和查询提交 [16]。这些查询测试顺序和索引扫描访问路径,并常规地暴露 CPU、IO 或内存绑定的关系运算符。

图 10 显示了在分析中经常使用的数据库中顺序执行所有 ClickBench 查询的总相对冷和热运行时间。测量是在单节点 AWS EC2 c6a.4xlarge 实例上进行的,该实例具有 16 个 vCPU、32 GB RAM 和 5000 IOPS / 1000 MiB/s 磁盘。与之相似的系统用于 Redshift (ra3.4xlarge,12 vCPUs,96 GB RAM) 和 Snowfake (仓库大小 S:2x8 vCPUs,2x16 GB RAM)。物理数据库设计仅进行了轻微的调整,例如,我们指定了主键,但不更改单个列的压缩、创建投影或跳过索引。我们还在每次冷查询运行之前刷新 Linux 页面缓存,但不调整数据库或操作系统参数。对于每个查询,使用数据库中最快的运行时间作为基准。其他数据库的相对查询运行时间计算为 ( + 10)/(_ + 10)。数据库的总相对运行时间是每个查询比率的几何平均数。虽然研究数据库 Umbra [54] 实现了最佳的整体热运行时间,ClickHouse 在热和冷运行时间上优于所有其他生产级数据库。

图 10: ClickBench 的相对冷和热运行时间。

为了跟踪 SELECT 在更为多样的工作负载中随时间的性能,我们 使用 一种名为 VersionsBench 的四个基准组合 [19]。这个基准每个月执行一次,当新版本发布时评估其性能 [20] 并识别可能导致性能下降的代码变化:每个基准包括:1. ClickBench(上述描述),2. 15 个 MgBench [21] 查询,3. 针对 6 亿行的非规范化星型架构基准 [57] 的 13 个查询。4. 4 个针对 NYC Taxi Rides 的查询,该表有 34 亿行 [70]

图 11 显示了 2018 年 3 月至 2024 年 3 月间 77 个 ClickHouse 版本的 VersionsBench 运行时间的变化。为了补偿个别查询相对运行时间的差异,我们使用几何均值并以最小查询运行时间的比率作为权重对运行时间进行归一化。VersionBench 的性能在过去六年中提高了 1.72 倍。长期支持(LTS)版本的发布日期在 x 轴上标出。尽管在某些时期性能暂时下降,但 LTS 版本通常具有与前一个 LTS 版本相当或更好的性能。2022 年 8 月的显著改善是由第 4.4 节中描述的列按列过滤评估技术引起的。

图 11: 2018-2024 年 VersionsBench 的相对热运行时间。

6.2.2 规范化表

在经典的数据仓库中,数据通常使用星型或雪花架构进行建模。我们展示了 TPC-H 查询(规模因子 100)的运行时间,但指出规范化表是 ClickHouse 的新兴用例。图 12 显示了基于第 4.4 节中描述的并行哈希连接算法的 TPC-H 查询的热运行时间。这些测量是在单节点 AWS EC2 c6i.16xlarge 实例上进行的,该实例具有 64 个 vCPU、128 GB RAM 和 5000 IOPS / 1000 MiB/s 磁盘。记录了五次运行中最快的一次。为了参考,我们在一个规模相当的 Snowfake 系统中进行了相同的测量(仓库大小 L,8x8 vCPUs,8x16 GB RAM)。表中排除了 11 个查询的结果:查询 Q2、Q4、Q13、Q17 和 Q20-22 包含的相关子查询在 ClickHouse v24.6 中不受支持。查询 Q7-Q9 和 Q19 依赖于扩展的计划级连接优化,例如连接重排序和连接谓词下推(在 ClickHouse v24.6 中均缺失),以实现可用的运行时间。在 2024 年,计划实现自动子查询去相关化和更好的连接优化器支持 [18]。在剩余的 11 个查询中,5 个(6 个)查询在 ClickHouse 中执行得更快(Snowfake)。正如前面提到的,优化对于性能至关重要 [27],我们预计它们将在实施后进一步改善这些查询的运行时间。

图 12: TPC-H 查询的热运行时间(单位:秒)。

在最近几十年,分析数据库受到了极大的学术和商业关注 [1]。早期系统如 Sybase IQ [48]、Teradata [72]、Vertica [42] 和 Greenplum [47] 的特点是昂贵的批量 ETL 作业和由于其本地部署特性而导致的有限弹性。在 2010 年代初,云原生数据仓库和数据库即服务(DBaaS)提供的 Snowfake [22]、BigQuery [49] 和 Redshift [4] 的出现极大降低了组织进行分析的成本和复杂性,同时受益于高可用性和自动资源扩展。更近期,分析执行内核(例如 Photon [5] 和 Velox [62])提供了用于不同分析、流处理和机器学习应用的共同修改数据处理。

与 ClickHouse 在目标和设计原则上最相似的数据库是 Druid [78] 和 Pinot [34]。这两个系统的目标是实现高数据摄取速度的实时分析。与 ClickHouse 相似,表被分割为称为分段的横向部分。虽然 ClickHouse 持续合并较小的部分并可选地使用第 3.3 节中的技术减少数据量,但 Druid 和 Pinot 的部分始终是不可变的。此外,Druid 和 Pinot 需要专用节点来创建、修改和搜索表,而 ClickHouse 则使用单一二进制文件完成这些任务。

Snowfake [22] 是一个流行的基于共享磁盘架构的专有云数据仓库。其将表划分为微分区的方法与 ClickHouse 中分片的概念相似。Snowfake 使用混合 PAX 页 [3] 进行持久化,而 ClickHouse 的存储格式严格采用列式存储。Snowfake 还强调使用自动创建的轻量级索引 [31, 51] 进行本地缓存和数据修剪,以获得良好的性能。与 ClickHouse 中的主键类似,用户可以选择创建聚集索引以共同定位具有相同值的数据。

Photon [5] 和 Velox [62] 是旨在作为复杂数据管理系统中的组件使用的查询执行引擎。这两个系统将查询计划作为输入,然后在本地节点上对 Parquet(Photon)或 Arrow(Velox)文件执行 [46]。ClickHouse 能够消耗和生成这些通用格式的数据,但更倾向于其原生文件格式进行存储。虽然 Velox 和 Photon 不优化查询计划(Velox 进行基本的表达式优化),但它们利用运行时自适应技术,例如根据数据特征动态切换计算内核。类似地,ClickHouse 中的计划操作符可以在运行时创建其他操作符,主要基于查询的内存消耗切换到外部聚合或连接操作符。Photon 论文指出,生成代码的设计 [38, 41, 53] 比解释型向量化设计 [11] 更难以开发和调试。Velox 在实验性支持代码生成,构建并链接从运行时生成的 C++ 代码生成的共享库,而 ClickHouse 则直接与 LLVM 的请求编译 API 进行交互。

DuckDB [67] 也旨在由主机进程嵌入,但此外还提供查询优化和事务。它是为混合 OLAP 查询与偶尔的 OLTP 语句而设计的。DuckDB 因此选择了 DataBlocks [43] 存储格式,该格式采用轻量级压缩方法,如顺序保持的字典或参考框架 [2],以在混合工作负载中实现良好的性能。相反,ClickHouse 针对追加操作用例进行了优化,即没有或很少的更新和删除。数据块使用重型技术如 LZ4 进行压缩,假设用户充分利用数据修剪来加快频繁查询,并且 I/O 成本在剩余查询中大于解压成本。DuckDB 还基于 Hyper 的 MVCC 方案 [55] 提供可序列化事务,而 ClickHouse 仅提供快照隔离。

8 结论与展望

我们展示了 ClickHouse 的架构,这是一款开源的高性能 OLAP 数据库。凭借优化写入的存储层和先进的矢量化查询引擎,ClickHouse 能够在高摄取率的 PB 级数据集上实现实时分析。通过在后台异步合并和转换数据,ClickHouse 有效地解耦了数据维护与并行插入。其存储层通过稀疏主索引、跳过索引和投影表实现了积极的数据修剪。我们描述了 ClickHouse 对更新和删除的实现、幂等插入以及跨节点的数据复制以实现高可用性。查询处理层使用丰富的技术优化查询,并在所有服务器和集群资源中实现并行执行。集成表引擎和函数提供了一种方便的方法,与其他数据管理系统和数据格式进行无缝交互。通过基准测试,我们证明 ClickHouse 是市场上最快的分析数据库之一,并显示了 ClickHouse 在实际部署中典型查询性能的显著改善。

所有计划于 2024 年实现的功能和增强功能可以在公共路线图中找到 [18]。计划中的改进包括对用户事务的支持、PromQL [69] 作为替代查询语言、用于半结构化数据(例如 JSON)的新数据类型、对连接的更好的计划级优化,以及实现轻量级更新以补充轻量级删除的计划。

鸣谢

根据版本 24.6,SELECT * FROM system.contributors 返回 1994 名为 ClickHouse 贡献的人士。我们要感谢 ClickHouse Inc. 的整个工程团队和 ClickHouse 出色的开源社区为共同构建这个数据库所付出的努力和奉献。

参考文献

  • [1] Daniel Abadi, Peter Boncz, Stavros Harizopoulos, Stratos Idreaos, 和 Samuel Madden. 2013. 现代列式数据库系统的设计与实现. https://doi.org/10.1561/9781601987556
  • [2] Daniel Abadi, Samuel Madden, 和 Miguel Ferreira. 2006. 在列式数据库系统中集成压缩与执行. 载于2006年ACM SIGMOD国际数据管理会议论文集(SIGMOD '06). 671–682. https://doi.org/10.1145/1142473.1142548
  • [3] Anastassia Ailamaki, David J. DeWitt, Mark D. Hill, 和 Marios Skounakis. 2001. 关系织造以提高缓存性能. 载于第27届国际超大型数据库会议(VLDB '01)论文集. Morgan Kaufmann Publishers Inc., San Francisco, CA, USA, 169–180.
  • [4] Nikos Armenatzoglou, Sanuj Basu, Naga Bhanoori, Mengchu Cai, Naresh Chainani, Kiran Chinta, Venkatraman Govindaraju, Todd J. Green, Monish Gupta, Sebastian Hillig, Eric Hotinger, Yan Leshinksy, Jintian Liang, Michael McCreedy, Fabian Nagel, Ippokratis Pandis, Panos Parchas, Rahul Pathak, Orestis Polychroniou, Foyzur Rahman, Gaurav Saxena, Gokul Soundararajan, Sriram Subramanian, 和 Doug Terry. 2022. 亚马逊 Redshift 的重新设计. 载于2022年国际数据管理会议(SIGMOD '22)论文集, 费城, PA, USA. Association for Computing Machinery, New York, NY, USA, 2205–2217. https://doi.org/10.1145/3514221.3526045
  • [5] Alexander Behm, Shoumik Palkar, Utkarsh Agarwal, Timothy Armstrong, David Cashman, Ankur Dave, Todd Greenstein, Shant Hovsepian, Ryan Johnson, Arvind Sai Krishnan, Paul Leventis, Ala Luszczak, Prashanth Menon, Mostafa Mokhtar, Gene Pang, Sameer Paranjpye, Greg Rahn, Bart Samwel, Tom van Bussel, Herman van Hovell, Maryann Xue, Reynold Xin, 和 Matei Zaharia. 2022. Photon: 用于湖屋系统的快速查询引擎(SIGMOD '22). Association for Computing Machinery, New York, NY, USA, 2326–2339. https://doi.org/10.1145/3514221. 3526054
  • [6] Philip A. Bernstein 和 Nathan Goodman. 1981. 分布式数据库系统中的并发控制. ACM 计算机调查 13, 2 (1981), 185–221. https://doi.org/10.1145/356842.356846
  • [7] Spyros Blanas, Yinan Li, 和 Jignesh M. Patel. 2011. 为多核CPU设计和评估主内存哈希连接算法. 载于2011年ACM SIGMOD国际数据管理会议论文集(雅典, 希腊) (SIGMOD '11). Association for Computing Machinery, New York, NY, USA, 37–48. https://doi.org/10.1145/1989323.1989328
  • [8] Daniel Gomez Blanco. 2023. 实用的OpenTelemetry. Springer Nature.
  • [9] Burton H. Bloom. 1970. 允许错误的哈希编码中的空间/时间权衡. Commun. ACM 13, 7 (1970), 422–426. https://doi.org/10.1145/362686. 362692
  • [10] Peter Boncz, Thomas Neumann, 和 Orri Erling. 2014. TPC-H分析:从一个影响力基准中发现的隐含信息与经验教训. 在性能特征与基准评估中. 61–76. https://doi.org/10.1007/978-3-319- 04936-6_5
  • [11] Peter Boncz, Marcin Zukowski, 和 Niels Nes. 2005. MonetDB/X100:超流水线查询执行. 载于CIDR.
  • [12] Martin Burtscher 和 Paruj Ratanaworabhan. 2007. 双精度浮点数据的高吞吐量压缩. 载于数据压缩会议(DCC). 293–302. https://doi.org/10.1109/DCC.2007.44
  • [13] Jef Carpenter 和 Eben Hewitt. 2016. Cassandra: 权威指南(第2版). O'Reilly Media, Inc.
  • [14] Bernadette Charron-Bost, Fernando Pedone, 和 André Schiper (编辑). 2010. 复制:理论与实践. Springer-Verlag.
  • [15] chDB. 2024. chDB - 嵌入式OLAP SQL引擎. 访问日期2024-06-20,链接:https://github.com/chdb-io/chdb
  • [16] ClickHouse. 2024. ClickBench:用于分析数据库的基准测试. 访问日期2024-06-20,链接:https://github.com/ClickHouse/ClickBench
  • [17] ClickHouse. 2024. ClickBench:比较测量. 访问日期2024-06-20,链接:https://benchmark.clickhouse.com
  • [18] ClickHouse. 2024. ClickHouse 2024路线图(GitHub). 访问日期2024-06-20,链接:https://github.com/ClickHouse/ClickHouse/issues/58392
  • [19] ClickHouse. 2024. ClickHouse版本基准测试. 访问日期2024-06-20,链接:https://github.com/ClickHouse/ClickBench/tree/main/versions
  • [20] ClickHouse. 2024. ClickHouse版本基准测试结果. 访问日期2024-06-20,链接:https://benchmark.clickhouse.com/versions/
  • [21] Andrew Crotty. 2022. MgBench. 访问日期2024-06-20,链接:https://github.com/ andrewcrotty/mgbench
  • [22] Benoit Dageville, Thierry Cruanes, Marcin Zukowski, Vadim Antonov, Artin Avanes, Jon Bock, Jonathan Claybaugh, Daniel Engovatov, Martin Hentschel, Jiansheng Huang, Allison W. Lee, Ashish Motivala, Abdul Q. Munir, Steven Pelley, Peter Povinec, Greg Rahn, Spyridon Triantafyllis, 和 Philipp Unterbrunner. 2016. Snowflake弹性数据仓库. 载于2016年国际数据管理会议论文集(SIGMOD '16). Association for Computing Machinery, New York, NY, USA, 215–226. https: //doi.org/10.1145/2882903.2903741
  • [23] Patrick Damme, Annett Ungethüm, Juliana Hildebrandt, Dirk Habich, 和 Wolfgang Lehner. 2019. 从全面的实验调查到基于成本的轻量级整数压缩算法选择策略. ACM 数据库系统交易 44, 3, 第9号(2019), 46页. https://doi.org/10.1145/3323991
  • [24] Philippe Dobbelaere 和 Kyumars Sheykh Esmaili. 2017. Kafka与RabbitMQ:两种行业参考发布/订阅实现的比较研究:行业论文(DEBS '17). Association for Computing Machinery, New York, NY, USA, 227–238. https://doi.org/10.1145/3093742.3093908
  • [25] LLVM文档. 2024. LLVM中的自动向量化. 访问日期2024-06-20,链接:https://llvm.org/docs/Vectorizers.html
  • [26] Siying Dong, Andrew Kryczka, Yanqin Jin, 和 Michael Stumm. 2021. RocksDB:服务于大规模应用程序的发展优先级演变. ACM 存储交易 17, 4, 第26号(2021), 32页. https://doi.org/10.1145/3483840
  • [27] Markus Dreseler, Martin Boissier, Tilmann Rabl, 和 Matthias Ufacker. 2020. 定量分析TPC-H瓶颈及其优化. VLDB 基金会公报 13, 8 (2020), 1206–1220. https://doi.org/10.14778/3389133.3389138
  • [28] Ted Dunning. 2021. t-digest:高效的分布估计. 软件影响 7 (2021). https://doi.org/10.1016/j.simpa.2020.100049
  • [29] Martin Faust, Martin Boissier, Marvin Keller, David Schwalb, Holger Bischof, Katrin Eisenreich, Franz Färber, 和 Hasso Plattner. 2016. 在SAP HANA中使用哈希索引进行足迹减少和唯一性执行. 载于数据库和专家系统应用程序. 137–151. https://doi.org/10.1007/978-3-319-44406- 2_11
  • [30] Philippe Flajolet, Eric Fusy, Olivier Gandouet, 和 Frederic Meunier. 2007. HyperLogLog:一种近乎最佳基数估计算法的分析. 载于算法分析的会议,DMTCS 论文集,第AH卷, 2007年算法分析会议(AofA 07). 离散数学与理论计算机科学, 137–156. https://doi.org/10.46298/dmtcs.3545
  • [31] Hector Garcia-Molina, Jefrey D. Ullman, 和 Jennifer Widom. 2009. 数据库系统 - 完整书籍(第2版).
  • [32] Pawan Goyal, Harrick M. Vin, 和 Haichen Chen. 1996. 启动时间公平排队:一种用于综合服务分组交换网络的调度算法. 26, 4 (1996), 157–168. https://doi.org/10.1145/248157.248171
  • [33] Goetz Graefe. 1993. 大型数据库的查询评估技术. ACM 计算机调查 25, 2 (1993), 73–169. https://doi.org/10.1145/152610.152611
  • [34] Jean-François Im, Kishore Gopalakrishna, Subbu Subramaniam, Mayank Shrivastava, Adwait Tumbde, Xiaotian Jiang, Jennifer Dai, Seunghyun Lee, Neha Pawar, Jialiang Li, 和 Ravi Aringunram. 2018. Pinot:为530万用户提供实时OLAP. 载于2018年国际数据管理会议(SIGMOD '18)论文集,休斯顿, TX, USA. Association for Computing Machinery, New York, NY, USA, 583–594. https://doi.org/10.1145/3183713.3190661
  • [35] ISO/IEC 9075-9:2001 2001. 信息技术 — 数据库语言 — SQL — 第9部分:外部数据管理 (SQL/MED). 标准. 国际标准化组织.
  • [36] Paras Jain, Peter Kraft, Conor Power, Tathagata Das, Ion Stoica, 和 Matei Zaharia. 2023. 分析和比较湖屋存储系统. CIDR.
  • [37] Project Jupyter. 2024. Jupyter Notebooks. 访问日期2024-06-20,链接:https: //jupyter.org/
  • [38] Timo Kersten, Viktor Leis, Alfons Kemper, Thomas Neumann, Andrew Pavlo, 和 Peter Boncz. 2018. 关于编译和向量查询的所有你想知道但又害怕问的问题. Proc. VLDB Endow. 11, 13 (sep 2018), 2209–2222. https://doi.org/10.14778/3275366.3284966
  • [39] Changkyu Kim, Jatin Chhugani, Nadathur Satish, Eric Sedlar, Anthony D. Nguyen, Tim Kaldewey, Victor W. Lee, Scott A. Brandt, 和 Pradeep Dubey. 2010. FAST:在现代CPU和GPU上的快速架构敏感树搜索. 载于2010年ACM SIGMOD国际数据管理会议论文集(印第安纳波利斯, 印第安纳, USA) (SIGMOD '10). Association for Computing Machinery, New York, NY, USA, 339–350. https://doi.org/10.1145/1807167.1807206
  • [40] Donald E. Knuth. 1973. 计算机程序艺术, 第三卷:排序与搜索. Addison-Wesley.
  • [41] André Kohn, Viktor Leis, 和 Thomas Neumann. 2018. 编译查询的自适应执行. 载于2018年IEEE第34届国际数据工程会议(ICDE). 197–208. https://doi.org/10.1109/ICDE.2018.00027
  • [42] Andrew Lamb, Matt Fuller, Ramakrishna Varadarajan, Nga Tran, Ben Vandiver, Lyric Doshi, 和 Chuck Bear. 2012. Vertica 分析数据库:C-Store 7年后的情况. Proc. VLDB Endow. 5, 12 (aug 2012), 1790–1801. https://doi.org/10. 14778/2367502.2367518
  • [43] Harald Lang, Tobias Mühlbauer, Florian Funke, Peter A. Boncz, Thomas Neumann, 和 Alfons Kemper. 2016. 数据块:使用向量化和编译的压缩存储上的混合OLTP和OLAP. 载于2016年国际数据管理会议(SIGMOD '16)论文集. Association for Computing Machinery, New York, NY, USA, 311–326. https://doi.org/10.1145/2882903.2882925
  • [44] Viktor Leis, Peter Boncz, Alfons Kemper, 和 Thomas Neumann. 2014. Morsel驱动的并行性:一种面向NUMA的查询评估框架。 载于2014年ACM SIGMOD国际数据管理会议(SGPOMD '14)论文集(斯诺伯德, 犹他, USA). Association for Computing Machinery, New York, NY, USA, 743–754. https://doi.org/10.1145/2588555. 2610507
  • [45] Viktor Leis, Alfons Kemper, 和 Thomas Neumann. 2013. 自适应基数树:为主内存数据库提供ARTful索引. 载于2013年IEEE第29届国际数据工程会议(ICDE). 38–49. https://doi.org/10.1109/ICDE. 2013.6544812
  • [46] Chunwei Liu, Anna Pavlenko, Matteo Interlandi, 和 Brandon Haynes. 2023. 深入探讨分析型DBMS的常见开放格式. 16, 11 (jul 2023), 3044–3056. https://doi.org/10.14778/3611479.3611507
  • [47] Zhenghua Lyu, Huan Hubert Zhang, Gang Xiong, Gang Guo, Haozhou Wang, Jinbao Chen, Asim Praveen, Yu Yang, Xiaoming Gao, Alexandra Wang, Wen Lin, Ashwin Agrawal, Junfeng Yang, Hao Wu, Xiaoliang Li, Feng Guo, Jiang Wu, Jesse Zhang, 和 Venkatesh Raghavan. 2021. Greenplum:一种用于事务和分析工作负载的混合数据库(SIGMOD '21). Association for Computing Machinery, New York, NY, USA, 2530–2542. https: //doi.org/10.1145/3448016.3457562
  • [48] Roger MacNicol 和 Blaine French. 2004. Sybase IQ Multiplex - 为分析而设计. 载于第三十届国际超大型数据库会议论文集(多伦多, 加拿大) (VLDB '04). VLDB 基金会, 1227–1230.
  • [49] Sergey Melnik, Andrey Gubarev, Jing Jing Long, Geofrey Romer, Shiva Shivakumar, Matt Tolton, Theo Vassilakis, Hossein Ahmadi, Dan Delorey, Slava Min, Mosha Pasumansky, 和 Jef Shute. 2020. Dremel:十年网络规模的交互式SQL分析. Proc. VLDB Endow. 13, 12 (aug 2020), 3461–3472. https://doi.org/10.14778/3415478.3415568
  • [50] Microsoft. 2024. Kusto查询语言. 访问日期2024-06-20,链接:https: //github.com/microsoft/Kusto-Query-Language
  • [51] Guido Moerkotte. 1998. 小型物化聚合:数据仓库中的轻量级索引结构. 载于第24届国际超大型数据库会议论文集(VLDB '98). 476–487.
  • [52] Jalal Mostafa, Sara Wehbi, Suren Chilingaryan, 和 Andreas Kopmann. 2022. SciTS:用于科学实验和工业物联网的时间序列数据库基准. 在第34届国际科学和统计数据库管理会议(SSDBM '22)中发表. 第12号文章. https: //doi.org/10.1145/3538712.3538723
  • [53] Thomas Neumann. 2011. 为现代硬件高效编译高效查询计划. Proc. VLDB Endow. 4, 9 (jun 2011), 539–550. https://doi.org/ 2002938.2002940
  • [54] Thomas Neumann 和 Michael J. Freitag. 2020. Umbra:一种具有内存性能的基于磁盘的系统. 在2020年第10届创新数据系统研究会议上,CIDR 2020,阿姆斯特丹,荷兰,2020年1月12-15日,在线会议记录. www.cidrdb.org. http://cidrdb.org/cidr2020/papers/p29-neumanncidr20.pdf
  • [55] Thomas Neumann, Tobias Mühlbauer, 和 Alfons Kemper. 2015. 适用于主内存数据库系统的快速可串行化多版本并发控制. 载于2015年ACM SIGMOD国际数据管理会议论文集(墨尔本, 维多利亚, 澳大利亚)(SIGMOD '15). Association for Computing Machinery, New York, NY, USA, 677–689. https://doi.org/10.1145/2723372. 2749436
  • [56] LevelDB on GitHub. 2024. LevelDB. 访问日期2024-06-20,链接:https://github. com/google/leveldb
  • [57] Patrick O'Neil, Elizabeth O'Neil, Xuedong Chen, 和 Stephen Revilak. 2009. 星形模式基准与增强的事实表索引. 载于性能评估与基准. Springer Berlin Heidelberg, 237–252. https: //doi.org/10.1007/978-3-642-10424-4_17
  • [58] Patrick E. O'Neil, Edward Y. C. Cheng, Dieter Gawlick, 和 Elizabeth J. O'Neil. 1996. 日志结构合并树(LSM-tree). 信息学学报 33 (1996), 351–385. https://doi.org/10.1007/s002360050048
  • [59] Diego Ongaro 和 John Ousterhout. 2014. 在可理解的共识算法中寻找一种解决方案. 载于2014年USENIX年会技术会议论文集(USENIX ATC'14). 305–320. https://doi.org/doi/10. 5555/2643634.2643666
  • [60] Patrick O'Neil, Edward Cheng, Dieter Gawlick, 和 Elizabeth O'Neil. 1996. 日志结构合并树(LSM-Tree). 信息学学报 33, 4 (1996), 351–385. https: //doi.org/10.1007/s002360050048
  • [61] Pandas. 2024. Pandas数据框. 访问日期2024-06-20,链接:https://pandas. pydata.org/
  • [62] Pedro Pedreira, Orri Erling, Masha Basmanova, Kevin Wilfong, Laith Sakka, Krishna Pai, Wei He, 和 Biswapesh Chattopadhyay. 2022. Velox:Meta的统一执行引擎. Proc. VLDB Endow. 15, 12 (aug 2022), 3372–3384. https: //doi.org/10.14778/3554821.3554829
  • [63] Tuomas Pelkonen, Scott Franklin, Justin Teller, Paul Cavallaro, Qi Huang, Justin Meza, 和 Kaushik Veeraraghavan. 2015. Gorilla:一个快速、可扩展的内存时间序列数据库. VLDB 基金会公报 8, 12 (2015), 1816–1827. https://doi.org/10.14778/2824032.2824078
  • [64] Orestis Polychroniou, Arun Raghavan, 和 Kenneth A. Ross. 2015. 重新思考内存数据库的SIMD向量化. 载于2015年ACM SIGMOD国际数据管理会议论文集(SIGMOD '15). 1493–1508. https://doi.org/10.1145/2723372.2747645
  • [65] PostgreSQL. 2024. PostgreSQL - 外部数据封装. 访问日期2024-06-20,链接:https://wiki.postgresql.org/wiki/Foreign_data_wrappers
  • [66] Mark Raasveldt, Pedro Holanda, Tim Gubner, 和 Hannes Mühleisen. 2018. 公平基准测试被认为困难:数据库性能测试中的常见陷阱. 载于数据库系统测试研讨会论文集(休斯顿, TX, USA) (DBTest'18). 第2号文章, 6页. https://doi.org/10.1145/3209950.3209955
  • [67] Mark Raasveldt 和 Hannes Mühleisen. 2019. DuckDB:一种可嵌入的分析数据库(SIGMOD '19). Association for Computing Machinery, New York, NY, USA, 1981–1984. https://doi.org/10.1145/3299869.3320212
  • [68] Jun Rao 和 Kenneth A. Ross. 1999. 面向决策支持的缓存意识索引. 载于第25届国际超大型数据库会议论文集(VLDB '99). San Francisco, CA, USA, 78–89.
  • [69] Navin C. Sabharwal 和 Piyush Kant Pandey. 2020. 使用Prometheus查询语言(PromQL). 载于监控微服务和容器化应用程序. https://doi.org/10.1007/978-1-4842-6216-0_5
  • [70] Todd W. Schneider. 2022. 纽约市出租车和按需车辆数据. 访问日期2024-06-20,链接:https://github.com/toddwschneider/nyc-taxi-data
  • [71] Mike Stonebraker, Daniel J. Abadi, Adam Batkin, Xuedong Chen, Mitch Cherniack, Miguel Ferreira, Edmond Lau, Amerson Lin, Sam Madden, Elizabeth O'Neil, Pat O'Neil, Alex Rasin, Nga Tran, 和 Stan Zdonik. 2005. C-Store:一种列式数据库管理系统. 载于第31届国际超大型数据库会议论文集(VLDB '05). 553–564.
  • [72] Teradata. 2024. Teradata数据库. 访问日期2024-06-20,链接:https://www. teradata.com/resources/datasheets/teradata-database
  • [73] Frederik Transier. 2010. 面向内存文本搜索引擎的算法与数据结构. 博士 dissertation. https://doi.org/10.5445/IR/1000015824
  • [74] Adrian Vogelsgesang, Michael Haubenschild, Jan Finis, Alfons Kemper, Viktor Leis, Tobias Muehlbauer, Thomas Neumann, 和 Manuel Then. 2018. 实事求是:基准测试如何未能代表现实世界. 载于数据库系统测试研讨会论文集(休斯顿, TX, USA) (DBTest'18). 第1号文章, 6页. https://doi.org/10.1145/3209950.3209952
  • [75] LZ4网站. 2024. LZ4. 访问日期2024-06-20,链接:https://lz4.org/
  • [76] PRQL网站. 2024. PRQL. 访问日期2024-06-20,链接:https://prql-lang.org [77] Till Westmann, Donald Kossmann, Sven Helmer, 和 Guido Moerkotte. 2000. 压缩数据库的实现与性能. SIGMOD 记录.
  • 29, 3 (sep 2000), 55–67. https://doi.org/10.1145/362084.362137 [78] Fangjin Yang, Eric Tschetter, Xavier Léauté, Nelson Ray, Gian Merlino, 和 Deep Ganguli. 2014. Druid:实时分析数据存储. 载于2014年ACM SIGMOD国际数据管理会议论文集(斯诺伯德, 犹他, USA) (SIGMOD '14). Association for Computing Machinery, New York, NY, USA, 157–168. https://doi.org/10.1145/2588555.2595631
  • [79] Tianqi Zheng, Zhibin Zhang, 和 Xueqi Cheng. 2020. SAHA:针对分析数据库的字符串自适应哈希表. 应用科学 10, 6 (2020). https: //doi.org/10.3390/app10061915
  • [80] Jingren Zhou 和 Kenneth A. Ross. 2002. 使用SIMD指令实现数据库操作. 载于2002年ACM SIGMOD国际数据管理会议论文集(SIGMOD '02). 145–156. https://doi.org/10. 1145/564691.564709
  • [81] Marcin Zukowski, Sandor Heman, Niels Nes, 和 Peter Boncz. 2006. 超标量RAM-CPU缓存压缩. 载于第22届国际数据工程会议(ICDE '06)论文集. 59. https://doi.org/10.1109/ICDE. 2006.150