架构概述
这是我们 VLDB 2024 科学论文的网络版本。我们也在 博客上分享了它的背景和历程,并推荐观看 ClickHouse CTO 和创始人 Alexey Milovidov 在 VLDB 2024 上的演讲。
摘要
在过去的几十年里,存储和分析的数据量呈指数级增长。各行各业的企业开始依赖这些数据来改进产品、评估性能和做出关键业务决策。然而,随着数据量的日益增长,达到互联网规模,企业需要在经济高效和可扩展的方式下管理历史数据和新数据,同时使用大量的并发查询和期望实时延迟(例如,取决于用例,小于一秒)。
本文介绍了 ClickHouse,这是一款流行的开源 OLAP 数据库,专为对 PB 级数据集进行高性能分析而设计,具有高摄取速率。其存储层结合了基于传统 LSM 树的数据格式,以及对历史数据进行连续转换(例如,聚合、归档)的创新技术,这些技术在后台运行。查询使用方便的 SQL 方言编写,并由最先进的向量化查询执行引擎处理,可以选择编译代码。ClickHouse 大量使用剪枝技术来避免在查询中评估不相关的数据。其他数据管理系统可以在表函数、表引擎ClickHouse 中的表引擎决定了数据的写入、存储和访问方式。MergeTree 是最常用的表引擎,允许快速插入大量数据,这些数据在后台进行处理。或数据库引擎级别集成。
1 引言
本文描述了 ClickHouse,这是一种面向列的 OLAP 数据库,专为对包含数万亿行和数百列的表进行高性能分析查询而设计。ClickHouse 于 2009 年作为用于 Web 规模日志文件数据的筛选和聚合运算符而 启动,并在 2016 年开源。 图 1 说明了本文描述的主要功能引入 ClickHouse 的时间。
ClickHouse 旨在解决现代分析数据管理的五个关键挑战
-
海量数据集和高摄取速率。 许多以数据驱动的应用,例如 Web 分析、金融和电子商务,的特点是海量且持续增长的数据量。为了处理海量数据集,分析数据库不仅必须提供高效的索引和压缩策略,还必须允许跨多个节点分发数据(横向扩展),因为单个服务器的存储空间限制在几 TB 左右。此外,最近的数据通常比历史数据更适合实时洞察。因此,分析数据库必须能够以持续的高速率或突发速率摄取新数据,以及连续“降优先级”(例如,聚合、归档)历史数据,而不会降低并行报告查询的速度。
-
许多并发查询,并期望低延迟。 查询通常可以分为临时查询(例如,探索性数据分析)和定期查询(例如,定期仪表板查询)。用例越具交互性,查询延迟就越低,从而对查询优化和执行提出了挑战。 定期查询还提供了一种适应工作负载的物理数据库布局的机会。 因此,数据库应提供剪枝技术,以优化频繁查询。 根据查询优先级,数据库还必须为共享系统资源(例如 CPU、内存、磁盘和网络 I/O)提供相等或优先访问权限,即使同时运行大量查询也是如此。
-
多样化的数据存储、存储位置和格式。 为了与现有数据架构集成,现代分析数据库应具有高度的开放性,能够读取和写入任何系统、位置或格式的外部数据。
-
方便的查询语言,并支持性能内省。 OLAP 数据库的实际使用情况提出了额外的“软”要求。 例如,用户通常更喜欢使用具有嵌套数据类型和广泛的常规、聚合和窗口函数的表达 SQL 方言与数据库交互,而不是利基编程语言。 分析数据库还应提供复杂的工具来内省系统的性能或单个查询的性能。
-
行业级的健壮性和多功能部署。 由于商品硬件不可靠,数据库必须提供数据复制以提高对节点故障的鲁棒性。 此外,数据库应在任何硬件上运行,从旧笔记本电脑到功能强大的服务器。 最后,为了避免基于 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] 编写。
存储层由不同的表引擎组成,这些表引擎封装了表数据的格式和位置。 表引擎分为三类:第一类是 MergeTreeClickHouse 中的 MergeTree 是一种表引擎,专为高数据摄取速率和大数据量而设计。它是 ClickHouse 中的核心存储引擎,提供诸如列式存储、自定义分区、稀疏主索引以及支持后台数据合并等功能。* 系列表引擎,它们代表 ClickHouse 中的主要持久化格式。 基于 LSM 树的思想 [60],表被分成水平排序的 部分磁盘上的物理文件(或目录),用于存储表数据的一部分。这与分区不同,分区是使用分区键创建的表的逻辑划分。,这些部分由后台进程持续合并。 独立的 MergeTreeClickHouse 中的 MergeTree 是一种表引擎,专为高数据摄取速率和大数据量而设计。它是 ClickHouse 中的核心存储引擎,提供诸如列式存储、自定义分区、稀疏主索引以及支持后台数据合并等功能。* 表引擎的不同之处在于合并方式将来自其输入 部分磁盘上的物理文件(或目录),用于存储表数据的一部分。这与分区不同,分区是使用分区键创建的表的逻辑划分。的行组合在一起。 例如,如果过时,可以聚合或替换行。
第二类是用于加速或分发查询执行的专用表引擎。 此类别包括称为字典的内存键值表引擎。 字典 缓存定期针对内部或外部数据源执行的查询的结果。 这大大降低了在可以容忍一定程度的数据陈旧性的场景中的访问延迟。 其他专用表引擎的示例包括用于临时表的纯内存引擎和 分布式表ClickHouse 中的分布式表是一种特殊类型的表,它本身不存储数据,而是为集群中的多个服务器提供透明数据分片(见下文)的统一视图。引擎,用于透明数据分片(见下文)。
第三类表引擎是用于与外部系统(例如关系数据库(例如 PostgreSQL、MySQL)、发布/订阅系统(例如 Kafka、RabbitMQ [24])或键/值存储(例如 Redis))进行双向数据交换的虚拟表引擎。 虚拟引擎还可以与数据湖(例如 Iceberg、DeltaLake、Hudi [36])或对象存储中的文件(例如 AWS S3、Google GCP)交互。
ClickHouse 支持在多个 集群由协同工作以存储和处理数据的节点(服务器)组成的集合。 节点上对表进行分片和复制,以实现可扩展性和可用性。分片根据分片表达式将表划分为一组表分片。各个分片是相互独立的表,通常位于不同的节点上。客户端可以直接读取和写入分片,即将其视为单独的表,或者使用特殊的 Distributed 表引擎ClickHouse 中的表引擎决定了数据的写入、存储和访问方式。MergeTree 是最常用的表引擎,允许快速插入大量数据,这些数据在后台进行处理。,它提供所有表分片的全局视图。分片的主要目的是处理超出单个节点容量的数据集(通常是几百 TB 的数据)。分片的另一个用途是将表的读写负载分布到多个节点上,即负载均衡。与此正交的是,一个 分片数据的子集。ClickHouse 始终至少为您的数据提供一个分片。如果您不跨多个服务器拆分数据,您的数据将存储在一个分片中。跨多个服务器分片数据可用于在超过单个服务器的容量时划分负载。 可以在多个节点上复制,以容忍节点故障。为此,每个 Merge-Tree* 表引擎ClickHouse 中的表引擎决定了数据的写入、存储和访问方式。MergeTree 是最常用的表引擎,允许快速插入大量数据,这些数据在后台进行处理。 都有一个对应的 ReplicatedMergeTree* 引擎,它使用基于 Raft 共识 [59](由 Keeper,一种用 C++ 编写的 Apache Zookeeper 的即插即用替代品)的多主协调方案,以保证每个 分片数据的子集。ClickHouse 始终至少为您的数据提供一个分片。如果您不跨多个服务器拆分数据,您的数据将存储在一个分片中。跨多个服务器分片数据可用于在超过单个服务器的容量时划分负载。 始终具有可配置数量的副本。第 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 dataframes [61]。受 DuckDB [67] 的启发,chDB 将 ClickHouse 嵌入到宿主进程中,作为高性能 OLAP 引擎。与其他模式相比,这允许在相同的地址空间中有效地传递源数据和结果数据,而无需复制。
3 存储层
本节讨论 MergeTreeClickHouse 中的 MergeTree 是一种表引擎,专为高数据摄取速率和大数据量而设计。它是 ClickHouse 的核心存储引擎,提供诸如列式存储、自定义分区、稀疏主索引以及支持后台数据合并等功能。* 表引擎作为 ClickHouse 的原生存储格式。我们描述了它们的磁盘上表示形式,并讨论了 ClickHouse 中的三种数据修剪技术。之后,我们介绍了合并策略,这些策略在不影响同时插入的情况下持续转换数据。最后,我们解释了如何实现更新和删除,以及数据去重、数据复制和 ACID 合规性。
3.1 磁盘格式
在 MergeTreeClickHouse 中的 MergeTree 是一种表引擎,专为高数据摄取速率和大数据量而设计。它是 ClickHouse 的核心存储引擎,提供诸如列式存储、自定义分区、稀疏主索引以及支持后台数据合并等功能。* 表引擎ClickHouse 中的表引擎决定了数据的写入、存储和访问方式。MergeTree 是最常用的表引擎,允许快速插入大量数据,这些数据在后台进行处理。 中,每个表都组织为一组不可变的表 部分磁盘上的物理文件(或目录),用于存储表数据的一部分。这与分区不同,分区是使用分区键创建的表的逻辑划分。。每次将一组行插入到表中时,都会创建一个部分。 部分磁盘上的物理文件(或目录),用于存储表数据的一部分。这与分区不同,分区是使用分区键创建的表的逻辑划分。 是自包含的,这意味着它们包括解释其内容所需的所有元数据,而无需查阅中央目录。为了使每个表的 部分磁盘上的物理文件(或目录),用于存储表数据的一部分。这与分区不同,分区是使用分区键创建的表的逻辑划分。 数量保持较低,后台合并作业会定期将多个较小的 部分磁盘上的物理文件(或目录),用于存储表数据的一部分。这与分区不同,分区是使用分区键创建的表的逻辑划分。 合并成一个较大的部分,直到达到可配置的部分大小(默认值为 150 GB)。由于 部分磁盘上的物理文件(或目录),用于存储表数据的一部分。这与分区不同,分区是使用分区键创建的表的逻辑划分。 按表的 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多个行可以具有相同的主键值。 列排序(参见第 3.2 节),因此合并使用高效的 k 路合并排序 [40]。源 部分磁盘上的物理文件(或目录),用于存储表数据的一部分。这与分区不同,分区是使用分区键创建的表的逻辑划分。 被标记为非活动,并在其引用计数降至零(即没有进一步的查询从它们读取)后最终被删除。
可以以两种模式插入行:在同步插入模式下,每个 INSERT 语句都会创建一个新的部分并将其附加到表中。为了最大限度地减少合并的开销,建议数据库客户端批量插入元组,例如一次 20,000 行。但是,如果应实时分析数据,则客户端批量处理造成的延迟通常是不可接受的。例如,可观察性用例通常涉及数千个监控代理持续发送少量事件和指标数据。此类场景可以使用异步插入模式,在这种模式下,ClickHouse 将来自多个传入 INSERT 的行缓冲到同一个表中,并且仅当缓冲区大小超过可配置的阈值或超时到期时,才会创建一个新的部分。
图 3:MergeTreeClickHouse 中的 MergeTree 是一种表引擎,专为高数据摄取速率和大数据量而设计。它是 ClickHouse 的核心存储引擎,提供诸如列式存储、自定义分区、稀疏主索引以及支持后台数据合并等功能。* 引擎表的插入和合并。
图 3 说明了四次同步插入和两次异步插入到一个 MergeTreeMergeTree 是 ClickHouse 中的一种表引擎,专为高数据摄取速率和大数据量而设计。它是 ClickHouse 的核心存储引擎,提供诸如列式存储、自定义分区、稀疏主索引以及支持后台数据合并等功能。 引擎的表中。两次合并将活动的 partsparts 是存储在磁盘上的表数据的一部分的物理文件(或目录)。这与分区不同,分区是使用分区键创建的表的逻辑划分。 的数量从最初的五个减少到两个。
与 LSM 树 [58] 及其在各种数据库中的实现 [13, 26, 56] 相比,ClickHouse 将所有 partsparts 是存储在磁盘上的表数据的一部分的物理文件(或目录)。这与分区不同,分区是使用分区键创建的表的逻辑划分。 都视为相等,而不是以层次结构排列它们。因此,合并不再局限于同一级别的 partsparts 是存储在磁盘上的表数据的一部分的物理文件(或目录)。这与分区不同,分区是使用分区键创建的表的逻辑划分。。由于这也放弃了 partsparts 是存储在磁盘上的表数据的一部分的物理文件(或目录)。这与分区不同,分区是使用分区键创建的表的逻辑划分。 的隐式时间顺序,因此需要替代机制来进行不基于墓碑的更新和删除(参见第 3.4 节)。ClickHouse 将插入直接写入磁盘,而其他基于 LSM 树的存储通常使用预写日志(参见第 3.7 节)。
一个 part 对应于磁盘上的一个目录,包含每个列的一个文件。作为优化,小 part(默认小于 10 MB)的列连续存储在单个文件中,以提高读取和写入的空间局部性。part 的行进一步逻辑划分为 8192 条记录的组,称为 granules。一个 granulegranule 是未压缩块中的行批次。在读取数据时,ClickHouse 访问 granules,而不是单个行,这使得在分析工作负载中能够更快地处理数据。默认情况下,一个 granule 包含 8192 行。主索引包含每个 granule 的一个条目。 代表 ClickHouse 中扫描和索引查找运算符处理的最小不可分割的数据单元。然而,磁盘上数据的读取和写入并不是在 granulegranule 是未压缩块中的行批次。在读取数据时,ClickHouse 访问 granules,而不是单个行,这使得在分析工作负载中能够更快地处理数据。默认情况下,一个 granule 包含 8192 行。主索引包含每个 granule 的一个条目。 级别执行的,而是在块的粒度上执行的,块将一列中多个相邻的 granules 组合在一起。新的块是基于每个 blockblock 是组织数据处理和存储的逻辑单元。每个 block 包含以列式数据,这些数据一起处理以增强查询执行期间的性能。通过以 block 处理数据,ClickHouse 通过最大限度地减少缓存未命中并促进向量化执行来有效地利用 CPU 核心。ClickHouse 使用各种压缩算法,例如 LZ4、ZSTD 和 Delta,来压缩 block 中的数据。 的可配置字节大小(默认 1 MB)形成的,即 blockblock 是组织数据处理和存储的逻辑单元。每个 block 包含以列式数据,这些数据一起处理以增强查询执行期间的性能。通过以 block 处理数据,ClickHouse 通过最大限度地减少缓存未命中并促进向量化执行来有效地利用 CPU 核心。ClickHouse 使用各种压缩算法,例如 LZ4、ZSTD 和 Delta,来压缩 block 中的数据。 中的 granules 数量是可变的,并且取决于列的数据类型和分布。此外,block 还会被压缩以减少其大小和 I/O 成本。默认情况下,ClickHouse 使用 LZ4 [75] 作为通用压缩算法,但用户也可以指定专门的编解码器,例如 Gorilla [63] 或 FPC [12] 用于浮点数据。压缩算法也可以链式连接。例如,可以首先使用 delta 编码 [23] 减少数值中的逻辑冗余,然后执行重量级压缩,最后使用 AES 编解码器加密数据。block 在从磁盘加载到内存时会动态解压缩。为了能够在压缩后仍能快速随机访问单个 granules,ClickHouse 还会为每个列存储一个映射,该映射将每个 granulegranule 是未压缩块中的行批次。在读取数据时,ClickHouse 访问 granules,而不是单个行,这使得在分析工作负载中能够更快地处理数据。默认情况下,一个 granule 包含 8192 行。主索引包含每个 granule 的一个条目。 id 与包含其压缩 blockblock 是组织数据处理和存储的逻辑单元。每个 block 包含以列式数据,这些数据一起处理以增强查询执行期间的性能。通过以 block 处理数据,ClickHouse 通过最大限度地减少缓存未命中并促进向量化执行来有效地利用 CPU 核心。ClickHouse 使用各种压缩算法,例如 LZ4、ZSTD 和 Delta,来压缩 block 中的数据。 的偏移量以及未压缩 blockblock 是组织数据处理和存储的逻辑单元。每个 block 包含以列式数据,这些数据一起处理以增强查询执行期间的性能。通过以 block 处理数据,ClickHouse 通过最大限度地减少缓存未命中并促进向量化执行来有效地利用 CPU 核心。ClickHouse 使用各种压缩算法,例如 LZ4、ZSTD 和 Delta,来压缩 block 中的数据。 中的 granules 偏移量关联起来。
列可以进一步进行 dictionarydictionary 是键值对的映射,对于各种类型的参考列表很有用。它是一项强大的功能,允许在查询中有效地使用 dictionary,通常比使用参考表的 `JOIN` 更有效。 编码 [2, 77, 81] 或使用两种特殊的包装数据类型(Nullable(T))使其可为空。LowCardinality(T) 将原始列值替换为整数 id,从而显著减少具有少量唯一值的的数据存储开销。Nullable(T) 为列 T 添加一个内部位图,表示列值是否为 NULL。
最后,表可以使用任意分区表达式进行范围、哈希或轮询分区。为了启用分区修剪,ClickHouse 还会存储每个分区的分区表达式的最小值和最大值。用户还可以选择创建更高级的列统计信息(例如,HyperLogLog [30] 或 t-digest [28] 统计信息),这些统计信息还可以提供基数估计。
3.2 数据修剪
在大多数用例中,扫描 PB 级别的数据只是为了回答单个查询,速度太慢且成本太高。ClickHouse 支持三种数据修剪技术,这些技术允许在搜索过程中跳过大部分行,从而显著加快查询速度。
首先,用户可以为表定义一个 primary key在 ClickHouse 中,primary key 确定数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的 primary key 不强制唯一性——多行可以具有相同的 primary key 值。 索引。 primary key在 ClickHouse 中,primary key 确定数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的 primary key 不强制唯一性——多行可以具有相同的 primary key 值。 列确定每个 part 中行排序的顺序,即索引是局部聚簇的。ClickHouse 还会为每个 part 存储一个映射,从每个 granulegranule 是未压缩块中的行批次。在读取数据时,ClickHouse 访问 granules,而不是单个行,这使得在分析工作负载中能够更快地处理数据。默认情况下,一个 granule 包含 8192 行。主索引包含每个 granule 的一个条目。 的第一行的 primary key在 ClickHouse 中,primary key 确定数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的 primary key 不强制唯一性——多行可以具有相同的 primary key 值。 列值到 granulegranule 是未压缩块中的行批次。在读取数据时,ClickHouse 访问 granules,而不是单个行,这使得在分析工作负载中能够更快地处理数据。默认情况下,一个 granule 包含 8192 行。主索引包含每个 granule 的一个条目。 的 id,即索引是稀疏的 [31]。由此产生的数据结构通常足够小,可以完全保留在内存中,例如,只需要 1000 个条目就可以索引 810 万行。 primary key在 ClickHouse 中,primary key 确定数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的 primary key 不强制唯一性——多行可以具有相同的 primary key 值。 的主要目的是使用二分查找而不是顺序扫描来评估经常过滤的列的相等性和范围谓词(第 4.4 节)。局部排序还可以用于 part 合并和查询优化,例如基于排序的聚合或当 primary key在 ClickHouse 中,primary key 确定数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的 primary key 不强制唯一性——多行可以具有相同的 primary key 值。 列形成排序列的前缀时从物理执行计划中删除排序运算符。
图 4 显示了一个在具有页面展示统计信息的表中,`EventTime` 列上的 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建稀疏索引以加速查询过滤。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。 索引。查询中与范围谓词匹配的颗粒可以通过二进制搜索 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建稀疏索引以加速查询过滤。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。 索引找到,而不是顺序扫描 `EventTime`。
图 4:使用 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建稀疏索引以加速查询过滤。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。 索引评估过滤器。
其次,用户可以创建 表投影 (table projections),即表的替代版本,其中包含按不同的 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建稀疏索引以加速查询过滤。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。 [71] 排序的相同行。投影允许加速过滤不同于主表 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建稀疏索引以加速查询过滤。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。 的查询,但代价是插入、合并和空间消耗的开销增加。默认情况下,投影仅从插入到主表的 分片 (parts)磁盘上的物理文件(或目录),存储表的全部或部分数据。这与分区不同,分区是使用分区键创建的表的逻辑划分。 中惰性地填充,而不是从现有的 分片 (parts)磁盘上的物理文件(或目录),存储表的全部或部分数据。这与分区不同,分区是使用分区键创建的表的逻辑划分。 中填充,除非用户完全物化 投影 (projection)ClickHouse 中的投影是一种隐藏的、自动维护的表,它以不同的顺序或使用预计算的聚合来存储数据,以加速查询,特别是那些过滤不在主键中的列的查询。。查询优化器根据估计的 I/O 成本选择从主表或 投影 (projection)ClickHouse 中的投影是一种隐藏的、自动维护的表,它以不同的顺序或使用预计算的聚合来存储数据,以加速查询,特别是那些过滤不在主键中的列的查询。 读取。如果对于某个分片不存在 投影 (projection)ClickHouse 中的投影是一种隐藏的、自动维护的表,它以不同的顺序或使用预计算的聚合来存储数据,以加速查询,特别是那些过滤不在主键中的列的查询。,则查询执行将回退到相应的原始表分片。
第三,跳过索引 (skipping indices) 为投影提供了一种轻量级的替代方案。跳过索引的思想是在多个连续的颗粒级别存储少量元数据,从而避免扫描不相关的行。跳过索引可以为任意索引表达式创建,并使用可配置的粒度,即 跳过索引 (skipping index)跳过索引用于在多个连续的颗粒级别存储少量元数据,从而允许 ClickHouse 避免扫描不相关的行。跳过索引为投影提供了一种轻量级的替代方案。 块中的颗粒数。可用的 跳过索引 (skipping index)跳过索引用于在多个连续的颗粒级别存储少量元数据,从而允许 ClickHouse 避免扫描不相关的行。跳过索引为投影提供了一种轻量级的替代方案。 类型包括:1. 最小值-最大值索引 [51],存储每个索引 块 (block)块是组织数据处理和存储的逻辑单元。每个块包含以列方式组织的数据,这些数据一起处理以提高查询执行期间的性能。通过以块处理数据,ClickHouse 通过最大限度地减少缓存未命中并促进矢量化执行来有效地利用 CPU 核心。ClickHouse 使用各种压缩算法,例如 LZ4、ZSTD 和 Delta,来压缩块中的数据。 的索引表达式的最小值和最大值。这种索引类型适用于局部聚类的数据,具有较小的绝对范围,例如松散排序的数据。2. 集合索引,存储可配置数量的唯一索引 块 (block)块是组织数据处理和存储的逻辑单元。每个块包含以列方式组织的数据,这些数据一起处理以提高查询执行期间的性能。通过以块处理数据,ClickHouse 通过最大限度地减少缓存未命中并促进矢量化执行来有效地利用 CPU 核心。ClickHouse 使用各种压缩算法,例如 LZ4、ZSTD 和 Delta,来压缩块中的数据。 值。这些索引最适合具有小局部基数的的数据,即“聚集在一起”的值。3. 布隆过滤器索引 [9] 为行、token 或 n-gram 值构建,具有可配置的误报率。这些索引支持文本搜索 [73],但与最小值-最大值和集合索引不同,它们不能用于范围或否定谓词。
3.3 合并时数据转换
商业智能和可观察性用例通常需要处理以持续高速率或突发方式生成的数据。此外,最近生成的数据通常比历史数据更相关,对于有意义的实时洞察更有价值。此类用例要求数据库在持续降低历史数据量(通过聚合或数据老化等技术)的同时,保持高数据摄取速率。ClickHouse 允许使用不同的合并策略对现有数据进行连续增量转换。合并时数据转换不会损害 INSERT 语句的性能,但不能保证表永远不包含不需要的(例如过时或未聚合的)值。如果需要,可以通过在 SELECT 语句中指定关键字 FINAL 来在查询时应用所有合并时转换。
替换合并 (Replacing merges) 根据其包含分片的创建时间戳保留最新插入的元组的版本,删除旧版本。如果元组具有相同 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建稀疏索引以加速查询过滤。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。 列值,则这些元组被认为是等效的。为了显式控制保留哪个元组,也可以指定一个特殊的版本列进行比较。替换合并通常用作合并时更新机制(通常用于更新频繁的用例),或作为插入时数据去重(第 3.5 节)的替代方案。
聚合合并 (Aggregating merges) 将具有相同 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建稀疏索引以加速查询过滤。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。 列值的行折叠到一个聚合行中。非 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建稀疏索引以加速查询过滤。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。 列必须是保存摘要值的部分聚合状态。两个部分聚合状态,例如 avg() 的求和和计数,被组合成一个新的部分聚合状态。聚合合并通常用于物化视图而不是普通表。物化视图基于对源表的转换查询填充。与其它数据库不同,ClickHouse 不会定期使用源表的全部内容刷新物化视图。相反,物化视图会在新分片插入到源表时,使用转换查询的结果进行增量更新。
图 5 显示了一个在具有页面展示统计信息的表上定义的 物化视图ClickHouse 中的物化视图是一种机制,它会在将数据插入到源表时自动运行查询,并将转换或聚合的结果存储在单独的目标表中,以加快查询速度。。对于插入到源表的新的 数据块数据块是磁盘上存储表数据一部分的物理文件(或目录)。这与分区不同,分区是使用分区键创建的表的逻辑划分。,转换查询会计算按区域分组的最大和平均延迟,并将结果插入到 物化视图ClickHouse 中的物化视图是一种机制,它会在将数据插入到源表时自动运行查询,并将转换或聚合的结果存储在单独的目标表中,以加快查询速度。 中。聚合函数 avg() 和 max() 带有扩展 -State 会返回部分聚合状态,而不是实际结果。为 物化视图ClickHouse 中的物化视图是一种机制,它会在将数据插入到源表时自动运行查询,并将转换或聚合的结果存储在单独的目标表中,以加快查询速度。 定义的聚合合并会持续组合不同 数据块数据块是磁盘上存储表数据一部分的物理文件(或目录)。这与分区不同,分区是使用分区键创建的表的逻辑划分。 中的部分聚合状态。要获得最终结果,用户可以使用 avg() 和 max()) 带有 -Merge 扩展在 物化视图ClickHouse 中的物化视图是一种机制,它会在将数据插入到源表时自动运行查询,并将转换或聚合的结果存储在单独的目标表中,以加快查询速度。 中合并部分聚合状态。
图 5:物化视图中的聚合合并。
TTL生存时间 (TTL) 是 ClickHouse 的一项功能,它会在一段时间后自动移动、删除或汇总列或行。这允许您更有效地管理存储,因为您可以删除、移动或存档不再需要频繁访问的数据。(生存时间)合并 为历史数据提供老化。与删除和聚合合并不同,TTL生存时间 (TTL) 是 ClickHouse 的一项功能,它会在一段时间后自动移动、删除或汇总列或行。这允许您更有效地管理存储,因为您可以删除、移动或存档不再需要频繁访问的数据。 合并一次只处理一个数据块。 TTL生存时间 (TTL) 是 ClickHouse 的一项功能,它会在一段时间后自动移动、删除或汇总列或行。这允许您更有效地管理存储,因为您可以删除、移动或存档不再需要频繁访问的数据。 合并是根据带有触发器和操作的规则定义的。触发器是一个为每一行计算时间戳的表达式,该时间戳与 TTL生存时间 (TTL) 是 ClickHouse 的一项功能,它会在一段时间后自动移动、删除或汇总列或行。这允许您更有效地管理存储,因为您可以删除、移动或存档不再需要频繁访问的数据。 合并运行的时间进行比较。虽然这允许用户以行粒度控制操作,但我们发现检查所有行是否满足给定条件并在整个数据块上运行操作就足够了。可能的动作包括 1. 将数据块移动到另一个卷(例如,更便宜、更慢的存储),2. 重新压缩数据块(例如,使用更重量级的编解码器),3. 删除数据块,以及 4. 汇总,即使用分组键和聚合函数聚合行。
例如,考虑在 列表 1 中给出的日志表定义。ClickHouse 会将时间戳列值超过一周的数据块移动到慢速但廉价的 S3 对象存储。
列表 1:在一周后将数据块移动到对象存储。
3.4 更新和删除
MergeTreeMergeTree 是 ClickHouse 中的表引擎,专为高数据摄取速率和大数据量而设计。它是 ClickHouse 的核心存储引擎,提供诸如列式存储、自定义分区、稀疏主索引以及对后台数据合并的支持等功能。* 表引擎的设计有利于仅追加的工作负载,但有些用例需要偶尔修改现有数据,例如为了遵守法规。存在两种更新或删除数据的方法,两者都不会 块块是组织数据处理和存储的逻辑单元。每个块包含以列式方式组织的数据,这些数据一起处理以提高查询执行期间的性能。通过以块的形式处理数据,ClickHouse 通过最大限度地减少缓存未命中并促进向量化执行来有效地利用 CPU 核心。ClickHouse 使用各种压缩算法(如 LZ4、ZSTD 和 Delta)来压缩块中的数据。 并行插入。
变异 (Mutations) 会原地重写表的全部 数据块数据块是磁盘上存储表数据一部分的物理文件(或目录)。这与分区不同,分区是使用分区键创建的表的逻辑划分。。为了防止表(删除)或列(更新)暂时加倍大小,此操作不是原子的,即并行 SELECT 语句可能会读取已变异和未变异的 数据块数据块是磁盘上存储表数据一部分的物理文件(或目录)。这与分区不同,分区是使用分区键创建的表的逻辑划分。。变异保证在操作结束时数据会被物理更改。删除变异仍然很昂贵,因为它们会重写所有列中的所有 数据块数据块是磁盘上存储表数据一部分的物理文件(或目录)。这与分区不同,分区是使用分区键创建的表的逻辑划分。。
作为替代方案,轻量级删除 (lightweight deletes) 仅更新内部位图列,指示是否删除了某一行。ClickHouse 会在 SELECT 查询中添加一个额外的位图列过滤器,以从结果中排除已删除的行。已删除的行仅在未来的常规合并中被物理删除。根据列数,轻量级删除可能比变异快得多,但 SELECT 的速度会变慢。
对同一表执行的更新和删除操作预计会很少且序列化,以避免逻辑冲突。
3.5 等幂插入
实践中经常出现的问题是客户端如何在将数据发送到服务器以插入到表中后处理连接超时。在这种情况下,客户端很难区分数据是否已成功插入。传统上,这个问题是通过从客户端重新发送数据到服务器并依赖 主键在 ClickHouse 中,主键确定数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多个行可以具有相同的主键值。 或唯一约束来拒绝重复插入来解决的。数据库使用基于二叉树 [39, 68]、基数树 [45] 或哈希表 [29] 的索引结构快速执行所需的点查找。由于这些数据结构索引每个元组,它们的空间和更新开销对于大型数据集和高摄取速率而言变得过于昂贵。
ClickHouse 提供了一种更轻量级的替代方案,其基于每次插入最终都会创建一个分片的事实。更具体地说,服务器会维护最近插入的 N 个分片的哈希值(例如 N=100),并忽略已知哈希值的分片的重新插入。非复制表和复制表的哈希值分别存储在 Keeper 中。因此,插入操作变得幂等的,即客户端可以在超时后简单地重新发送相同的行批次,并假设服务器负责去重。为了更精细地控制去重过程,客户端可以选择性地提供一个插入令牌,该令牌充当分片哈希值。虽然基于哈希的去重会产生与哈希新行相关的一些开销,但存储和比较哈希的成本可以忽略不计。
3.6 数据复制
复制是高可用性(容忍节点故障)的前提,同时也用于负载均衡和零停机升级 [14]。在 ClickHouse 中,复制基于表状态的概念,表状态由一组表分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.(第 3.1 节)和表元数据(例如列名和类型)组成。节点使用三种操作来推进表的状态:1. 插入操作在状态中添加一个新的分片,2. 合并操作添加一个新的分片并删除状态中的现有分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.,3. 突变和 DDL 语句添加分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.和/或删除分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.,和/或更改表元数据,具体取决于具体的操作。操作在单个节点上本地执行,并记录为全局复制日志中的状态转换序列。
复制日志由通常由三个 ClickHouse Keeper 进程组成的集合维护,这些进程使用 Raft 共识算法 [59] 为 ClickHouse 节点的集群A collection of nodes (servers) that work together to store and process data.提供分布式和容错的协调层。所有集群A collection of nodes (servers) that work together to store and process data.节点最初指向复制日志中的相同位置。当节点执行本地插入、合并、突变和 DDL 语句时,复制日志会在所有其他节点上异步重放。因此,复制的表只是最终一致的,即节点可能会暂时读取旧的表状态,同时收敛到最新的状态。上述大多数操作也可以同步执行,直到达到节点法定数量(例如,大多数节点或所有节点)采用新的状态。
例如,图 6 显示了一个初始为空的复制表,位于由三个 ClickHouse 节点组成的集群A collection of nodes (servers) that work together to store and process data.中。节点 1 首先接收两个插入语句,并将它们记录在存储在 Keeper 集合中的复制日志中 ( 1 2 )。接下来,节点 2 通过获取第一个日志条目 ( 3 ) 并从节点 1 下载新的分片 ( 4 ) 来重放第一个日志条目,而节点 3 重放两个日志条目 ( 3 4 5 6 )。最后,节点 3 将两个分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.合并到一个新的分片,删除输入分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.,并在复制日志中记录一个合并条目 ( 7 )。
图 6:由三个节点组成的集群A collection of nodes (servers) that work together to store and process data.中的复制。
存在三种优化方法来加速同步:首先,添加到集群A collection of nodes (servers) that work together to store and process data.的新节点不会从头开始重放复制日志,而是简单地复制写入最后一个复制日志条目的节点的的状态。其次,合并操作可以通过在本地重复它们或从另一个节点获取结果分片来重放。确切的行为是可配置的,允许平衡 CPU 消耗和网络 I/O。例如,跨数据中心的复制通常更喜欢本地合并以最大程度地降低运营成本。第三,节点并行重放相互独立的复制日志条目。这包括例如,连续插入到同一表的新的分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.的获取,或对不同表的操作。
3.7 ACID 合规性
为了最大限度地提高并发读写操作的性能,ClickHouse 尽可能避免使用锁。查询是对涉及的所有表的在查询开始时创建的所有分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.的快照执行的。这确保了新的分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.由并行 INSERT 或合并(第 3.1 节)插入不会参与执行。为了防止分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.同时被修改或删除(第 3.4 节),在查询持续时间内将处理的分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.的引用计数会增加。正式地说,这对应于由基于版本分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.的 MVCC 变体 [6] 实现的快照隔离。因此,语句通常不符合 ACID 标准,除非在快照拍摄时并发写入仅影响单个分片这一罕见情况下。
在实践中,ClickHouse 的大多数写入密集型决策用例甚至容忍在断电情况下丢失新数据的风险。数据库利用这一点,默认情况下不强制将新插入的分片A physical file (or directory) on disk that stores a portion of the table's data. This is different from a partition, which is a logical division of a table's data that is created using a partition key.写入磁盘(fsync),从而允许内核以牺牲原子性Atomicity ensures that a transaction (a series of database operations) is treated as a single, indivisible unit. This means that either all operations within the transaction occur, or none do. An example of an atomic transaction is transferring money from one bank account to another. If either step of the transfer fails, the transaction fails, and the money stays in the first account. Atomicity ensures no money is lost or created.为代价批量写入。
4 查询处理层
图 7:在 SIMD 单元、核心和节点上的并行化。
如图 7 所示,ClickHouse 在数据元素、数据块和表分片级别并行化查询。可以使用 SIMD 指令一次处理多个数据元素。在单个节点上,查询引擎在多个线程中同时执行运算符。ClickHouse 使用与 MonetDB/X100 [11] 相同的向量化模型,即运算符产生、传递和消耗多个行(数据块)而不是单行,以最大程度地减少虚拟函数调用的开销。如果源表分成不相交的表分片,则多个节点可以同时扫描这些分片。因此,充分利用了所有硬件资源,并且可以通过添加节点水平扩展查询处理,并通过添加核心垂直扩展查询处理。
本节的其余部分首先描述了数据元素、数据块和 分片数据的子集。ClickHouse 始终至少为您的数据提供一个分片。如果您不跨多个服务器拆分数据,您的数据将存储在一个分片中。跨多个服务器分片数据可用于在超过单个服务器容量时划分负载。 粒度上的并行处理的更多细节。然后,我们介绍选定的关键优化,以最大限度地提高查询性能。最后,我们讨论 ClickHouse 在同时查询存在的情况下如何管理共享系统资源。
4.1 SIMD 并行化
在操作符之间传递多行数据为矢量化创造了机会。矢量化要么基于手动编写的内联函数 [64, 80],要么基于编译器自动矢量化 [25]。从矢量化中受益的代码被编译成不同的计算内核。例如,查询操作符的内部热循环可以根据非矢量化内核、自动矢量化的 AVX2 内核和手动矢量化的 AVX-512 内核来实现。最快的内核是 在运行时 根据 cpuid 指令选择的。这种方法允许 ClickHouse 在 15 年前的系统上运行(最低要求 SSE 4.2),同时仍然可以在最新的硬件上提供显著的加速。
4.2 多核并行化
图 8:具有三个通道的物理操作符计划。
ClickHouse 遵循将 SQL 查询转换为物理计划操作符的有向图的传统方法 [31]。操作符计划的输入由特殊的源操作符表示,这些操作符以本机或任何受支持的第三方格式读取数据(参见第 5 节)。同样,特殊的 sink 操作符将结果转换为所需的输出格式。操作符物理计划在查询编译时展开为基于可配置的最大工作线程数(默认情况下,核心数)和源表大小的独立执行通道。通道将要处理的数据分解为不重叠的范围。为了最大限度地提高并行处理的机会,通道在尽可能晚的时候合并。
例如,图 8 中的 Node 1 的框显示了针对具有页面展示统计信息的表的典型 OLAP 查询的操作符图。在第一阶段,源表的三个不相交范围被同时过滤。Repartion 交换操作符动态地在第一阶段和第二阶段之间路由结果块,以保持处理线程的均匀利用率。如果扫描的范围具有显著不同的选择性,通道在第一阶段之后可能会变得不平衡。在第二阶段,幸存的行按 RegionID 分组。Aggregate 操作符维护具有 RegionID 作为分组列和每组求和计数作为部分聚合状态的局部结果组 avg()。局部聚合结果最终由 GroupStateMerge 操作符合并到全局聚合结果中。该操作符也是管道破坏器,即第三阶段只能在聚合结果完全计算完成后才能开始。在第三阶段,结果组首先由 Distribute 交换操作符划分为三个大小相等的不相交分区,然后按 AvgLatency 排序。排序分为三个步骤:首先,ChunkSort 操作符对每个分区的单个块进行排序。其次,StreamSort 操作符维护一个局部排序的结果,该结果与传入的排序块使用双向归并排序相结合。最后,MergeSort 操作符使用 k 路排序组合局部结果以获得最终结果。
操作符是状态机,并通过输入和输出端口相互连接。操作符的三种可能状态是 need-chunk、ready 和 done。要从 need-chunk 移动到 ready,将一个块放置在操作符的输入端口中。要从 ready 移动到 done,操作符处理输入块并生成输出块。要从 done 移动到 need-chunk,从操作符的输出端口移除输出块。两个连接的操作符中的第一次和第三次状态转换只能以组合步骤执行。源操作符(sink 操作符)只有 ready 和 done 状态(need-chunk 和 done)。
工作线程持续遍历物理操作符计划并执行状态转换。为了保持 CPU 缓存热,该计划包含提示,即同一线程应处理同一通道中的连续操作符。并行处理既发生在阶段内的不相交输入上(例如,在图 8 中,Aggregate 操作符并发执行),也在不被管道破坏器分隔的阶段上(例如,在图 8 中,Filter 和 Aggregate 操作符在同一通道中可以同时运行)。为了避免在新查询启动或并发查询完成时过度和不足订阅,可以在查询开始时为查询指定的最大工作线程数之间更改并行度(参见第 4.5 节)。
操作符还可以通过两种方式在运行时影响查询执行。首先,操作符可以动态创建和连接新的操作符。这主要用于在内存消耗超过可配置阈值时切换到外部聚合、排序或连接算法而不是取消查询。其次,操作符可以请求工作线程移动到异步队列。这在等待远程数据时可以更有效地利用工作线程。
ClickHouse 的查询执行引擎和 morsel 驱动的并行化 [44] 相似之处在于通道通常在不同的核心/NUMA 插槽上执行,并且工作线程可以从其他通道窃取任务。此外,没有中央调度组件;相反,工作线程通过持续遍历操作符计划来单独选择其任务。与 morsel 驱动的并行化不同,ClickHouse 将最大并行度嵌入到计划中,并与默认 morsel 大小(约 100,000 行)相比,使用更大的范围来划分源表。虽然这在某些情况下可能会导致停顿(例如,不同通道中 filter 操作符的运行时差异很大),但我们发现,至少通过在各个阶段使用 Repartition 等交换操作符,可以避免这种不平衡的累积。
4.3 多节点并行化
如果查询的源表被分片,则接收查询的节点上的查询优化器(发起节点)会尝试在其他节点上执行尽可能多的工作。来自其他节点的结果可以在查询计划的不同点集成。根据查询,远程节点可以 1. 将原始源表列流式传输到发起节点,2. 过滤源列并发送幸存的行,3. 执行过滤和聚合步骤并发送具有部分聚合状态的局部结果组,或 4. 运行整个查询,包括过滤、聚合和排序。
图 8 中的 Node 2 ... N 显示了在持有 hits 表分片的其他节点上执行的计划片段。这些节点过滤和分组本地数据并将结果发送到发起节点。发起节点 1 上的 GroupStateMerge 操作符合并本地和远程结果,然后对结果组进行最终排序。
4.4 全面性能优化
本节介绍应用于查询执行不同阶段的选定的关键性能优化。
查询优化。第一组优化应用于从查询的 AST 获取的语义查询表示之上。此类优化的示例包括常量折叠(例如,concat(lower('a'),upper('b')) 变为 'aB')、从某些聚合函数中提取标量(例如,sum(a*2) 变为 2 * sum(a))、公共子表达式消除以及将相等性过滤器的析取转换为 IN 列表(例如,x=c OR x=d 变为 x IN (c,d))。优化的语义查询表示随后转换为逻辑操作符计划。对逻辑计划进行的优化包括过滤下推、根据估计哪一个更昂贵重新排序函数评估和排序步骤。最后,逻辑查询计划转换为物理操作符计划。此转换可以利用所涉及的表引擎的特性。例如,在 MergeTreeClickHouse 中的 MergeTree 是一种表引擎,专为高数据摄取速率和大数据量而设计。它是 ClickHouse 的核心存储引擎,提供诸如列式存储、自定义分区、稀疏主索引以及对后台数据合并的支持等功能。-表引擎ClickHouse 中的表引擎决定了数据的写入、存储和访问方式。MergeTree 是最常用的表引擎,允许快速插入大量数据,这些数据在后台进行处理。的情况下,如果 ORDER BY 列形成 主键在 ClickHouse 中,主键确定数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多个行可以具有相同的主键值。的前缀,则可以按磁盘顺序读取数据,并且可以从计划中删除排序操作符。此外,如果聚合中的分组列形成 主键在 ClickHouse 中,主键确定数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多个行可以具有相同的主键值。的前缀,ClickHouse 可以使用排序聚合 [33],即直接聚合预排序输入中相同值的运行。与哈希聚合相比,排序聚合的内存占用明显更少,并且聚合值可以在处理完一个运行后立即传递给下一个操作符。
查询编译。ClickHouse 采用基于 LLVM 的查询编译,动态融合相邻的计划操作符 [38, 53]。例如,表达式 a * b + c + 1 可以组合成单个操作符,而不是三个操作符。除了表达式之外,ClickHouse 还使用编译来一次性评估多个聚合函数(即对于 GROUP BY)以及使用多个排序键进行排序。查询编译减少了虚拟调用的次数,将数据保存在寄存器或 CPU 缓存中,并帮助分支预测器,因为需要执行的代码更少。此外,运行时编译可以实现丰富的优化,例如编译器中实现的逻辑优化和窥孔优化,并可以访问最快的本地可用 CPU 指令。编译仅在相同的常规、聚合或排序表达式被不同的查询执行超过可配置的次数时启动。编译后的查询操作符会被缓存,并可供未来的查询重用。[7]
主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。索引评估。如果条件's conjunctive normal form 中过滤子句的子集构成主键列的前缀,ClickHouse 使用 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。索引来评估 WHERE 条件。主键索引从左到右,在词法排序的键值范围内进行分析。对应于 主键在 ClickHouse 中,主键决定了数据在磁盘上的存储顺序,并用于构建加速查询过滤的稀疏索引。与传统数据库不同,ClickHouse 中的主键不强制唯一性——多行可以具有相同的主键值。列的过滤子句使用三值逻辑进行评估——对于范围内的值,它们全部为真、全部为假或真/假混合。在后一种情况下,范围将被拆分为子范围,这些子范围将递归地进行分析。过滤条件中的函数存在额外的优化。首先,函数具有描述其单调性的特征,例如,toDayOfMonth(date) 在一个月内是分段单调的。单调性特征允许推断函数是否在排序的输入键值范围内产生排序结果。其次,某些函数可以计算给定函数结果的反函数。这用于将与常量比较函数调用替换为对键列的比较。例如,toYear(k) = 2024 可以替换为 k >= 2024-01-01 && k < 2025-01-01。
数据跳过。ClickHouse 尝试在查询运行时使用第 3.2 节中介绍的数据结构来避免数据读取。此外,不同列上的过滤器会根据启发式方法和(可选)列统计信息按降序估计选择性顺序进行评估。只有包含至少一行匹配行的的数据块才会被传递到下一个谓词。这逐渐减少了需要从谓词到谓词读取的数据量和要执行的计算量。仅当存在至少一个高度选择性的谓词时,才会应用此优化;否则,与并行评估所有谓词相比,查询的延迟会恶化。
哈希表。哈希表是聚合和哈希连接的基本数据结构。选择正确的哈希表类型对性能至关重要。ClickHouse 实例化 各种哈希表(截至 2024 年 3 月,超过 30 个),来自具有哈希函数、分配器、单元类型和调整策略作为变化点的通用哈希表模板。根据分组列的数据类型、估计的哈希表基数和其他因素,为每个查询操作符单独选择最快的哈希表。为哈希表实现的进一步优化包括
- 具有 256 个子表的两层布局(基于哈希的第一个字节),以支持巨大的键集,
- 具有四个子表和不同字符串长度的不同哈希函数的字符串哈希表 [79],
- 当只有少量键时,使用键直接作为桶索引(即,不进行哈希)的查找表,
- 具有嵌入式哈希的值,用于在比较成本高昂时(例如,字符串、AST)更快地解决冲突,
- 基于运行时统计信息预测的大小创建哈希表,以避免不必要的调整大小,
- 在单个内存 slab 上分配具有相同创建/销毁生命周期的多个小哈希表,
- 使用每哈希映射和每单元版本计数器清除哈希表以供重用,
- 使用 CPU 预取 (__builtin_prefetch) 来加速哈希键后检索值。
连接。由于 ClickHouse 最初仅支持基本的连接,因此许多用例历史上诉诸于反规范化的表。如今,该数据库 提供 SQL 中所有可用的连接类型(内部、左/右/全外部、交叉、as-of),以及不同的连接算法,例如哈希连接(朴素、grace)、排序合并连接和索引连接,用于具有快速键值查找的表引擎(通常是字典)。
由于连接是数据库中最昂贵的操作之一,因此提供经典连接算法的并行变体非常重要,理想情况下具有可配置的空间/时间权衡。对于哈希连接,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 内置性能分析工具
有各种工具可用于调查单个查询或后台操作中的性能瓶颈。用户通过基于系统表的统一接口与所有工具交互。
服务器和查询指标。服务器级别的统计信息(例如活动部件计数、网络吞吐量和缓存命中率)由每查询统计信息补充,例如读取的块数或索引使用情况统计信息。指标是同步(应要求)或异步地以可配置的间隔计算的。
采样分析器。可以使用采样分析器收集服务器线程的调用堆栈。结果可以可选地导出到外部工具,例如 flamegraph 可视化工具。
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 个 vCPU,96 GB RAM) 和 Snowfake (warehouse size S:2x8 vCPU,2x16 GB RAM) 使用了可比的系统。物理数据库设计仅经过轻微调整,例如,我们指定主键,但不更改单个列的压缩,创建投影或跳过索引。我们还在每次冷查询运行之前刷新 Linux 页面缓存,但不调整数据库或操作系统旋钮。对于每个查询,其他数据库的最快运行时间用作基线。其他数据库的相对查询运行时间计算为 ( + 10)/(_ + 10)。数据库的总相对运行时间是每查询比率的几何平均值。虽然研究数据库 Umbra [54] 实现了最佳的整体热运行时间,但 ClickHouse 在热运行时间和冷运行时间方面均优于所有其他生产级数据库。
图 10:ClickBench 的相对冷运行时间和热运行时间。
为了跟踪不同工作负载中 SELECT 的性能随时间的变化,我们 使用 称为 VersionsBench [19] 的四种基准测试的组合。该基准测试在发布新版本时每月执行一次,以评估其性能 [20] 并识别可能降低性能的代码更改:各个基准测试包括:1. ClickBench(如上所述),2. 15 MgBench [21] 查询,3. 针对反规范化星型模式基准测试 [57] 事实表(6 亿行)的 13 个查询。 4. 针对 纽约出租车行程(34 亿行)[70] 的 4 个查询。
图 11 显示了 2018 年 3 月至 2024 年 3 月期间 77 个 ClickHouse 版本的 VersionsBench 运行时间的发展。为了补偿单个查询运行时间的相对差异,我们使用几何平均值进行归一化,并以所有版本中最小查询运行时间的比率作为权重。在过去的六年里,VersionBench 的性能提高了 1.72 ×。x 轴上标出了具有长期支持 (LTS) 的版本的日期。尽管在某些时期性能暂时恶化,但 LTS 版本通常具有与以前的 LTS 版本相当或更好的性能。2022 年 8 月的重大改进是由第 4.4 节 中描述的按列过滤评估技术引起的。
图 11:VersionsBench 2018-2024 的相对热运行时间。
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 系统(warehouse size L,8x8 vCPU,8x16 GB RAM)中执行了相同的测量。将查询 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 查询的热运行时间(以秒为单位)。
7 相关工作
分析数据库一直是近几十年来学术界和商业界关注的焦点 [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 一样,表被分割成水平的 parts磁盘上存储表数据的一部分物理文件(或目录)。这与分区不同,分区是使用分区键创建的表数据的逻辑划分。,称为段。虽然 ClickHouse 会持续合并较小的 parts磁盘上存储表数据的一部分物理文件(或目录)。这与分区不同,分区是使用分区键创建的表数据的逻辑划分。,并可以选择使用第 3.3 节中的技术来减少数据量,但在 Druid 和 Pinot 中,parts磁盘上存储表数据的一部分物理文件(或目录)。这与分区不同,分区是使用分区键创建的表数据的逻辑划分。 始终保持不变。此外,Druid 和 Pinot 需要专门的节点来创建、修改和搜索表,而 ClickHouse 使用单体二进制文件来执行这些任务。
Snowflake [22] 是一种流行的专有云数据仓库,基于共享磁盘架构。它将表划分为微分区的方法类似于 ClickHouse 中的 parts磁盘上存储表数据的一部分物理文件(或目录)。这与分区不同,分区是使用分区键创建的表数据的逻辑划分。 概念。Snowflake 使用混合 PAX 页面 [3] 进行持久化,而 ClickHouse 的存储格式严格是列式的。Snowflake 还强调使用自动创建的轻量级索引进行本地缓存和数据修剪 [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] 也旨在由宿主进程嵌入,但它还提供查询优化和事务。它专为混合了偶尔的 OLTP 语句的 OLAP 查询而设计。因此,DuckDB 选择使用 DataBlocks [43] 存储格式,该格式采用轻量级压缩方法,例如保留顺序的字典或参考框架 [2],以在混合工作负载中实现良好的性能。相反,ClickHouse 针对仅追加用例进行了优化,即没有或很少的更新和删除。块使用 LZ4 等重量级技术进行压缩,假设用户充分利用数据修剪来加速频繁查询,并且对于剩余查询,I/O 成本超过了解压缩成本。DuckDB 还提供基于 Hyper 的 MVCC 方案的序列化事务 [55],而 ClickHouse 仅提供快照隔离。
8 结论与展望
我们介绍了 ClickHouse 的架构,这是一种开源、高性能 OLAP 数据库。凭借优化的写入存储层和最先进的向量化查询引擎为基础,ClickHouse 能够以高摄取速率对 PB 级数据集进行实时分析。通过异步地在后台合并和转换数据,ClickHouse 有效地将数据维护和并行插入解耦。其存储层能够使用稀疏主索引、跳过索引和 projectionClickHouse 中的投影是一种隐藏的、自动维护的表,它以不同的顺序或使用预计算的聚合存储数据,以加速查询,尤其是那些过滤掉主键中不存在的列的查询。 表进行积极的数据修剪。我们描述了 ClickHouse 的更新和删除实现、幂等插入以及跨节点的数据复制以实现高可用性。查询处理层使用大量技术优化查询,并在所有服务器和 cluster一组协同工作以存储和处理数据的节点(服务器)。 资源上并行执行。集成表引擎和函数提供了一种方便的方式来无缝地与其它数据管理系统和数据格式交互。通过基准测试,我们证明 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, and Samuel Madden. 2013. The Design and Implementation of Modern Column-Oriented Database Systems. https://doi.org/10.1561/9781601987556
- [2] Daniel Abadi, Samuel Madden, and Miguel Ferreira. 2006. Integrating Compression and Execution in Column-Oriented Database Systems. In Proceedings of the 2006 ACM SIGMOD International Conference on Management of Data (SIGMOD '06). 671–682.https://doi.org/10.1145/1142473.1142548
- [3] Anastassia Ailamaki, David J. DeWitt, Mark D. Hill, and Marios Skounakis. 2001. Weaving Relations for Cache Performance. In Proceedings of the 27th International Conference on Very Large Data Bases (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, and Doug Terry. 2022. Amazon Redshift Re-Invented. In Proceedings of the 2022 International Conference on Management of Data (Philadelphia, PA, USA) (SIGMOD '22). 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, and Matei Zaharia. 2022. Photon: A Fast Query Engine for Lakehouse Systems (SIGMOD '22). Association for Computing Machinery, New York, NY, USA, 2326–2339. https://doi.org/10.1145/3514221. 3526054
- [6] Philip A. Bernstein and Nathan Goodman. 1981. Concurrency Control in Distributed Database Systems. ACM Computing Survey 13, 2 (1981), 185–221. https://doi.org/10.1145/356842.356846
- [7] Spyros Blanas, Yinan Li, and Jignesh M. Patel. 2011. Design and evaluation of main memory hash join algorithms for multi-core CPUs. In Proceedings of the 2011 ACM SIGMOD International Conference on Management of Data (Athens, Greece) (SIGMOD '11). Association for Computing Machinery, New York, NY, USA, 37–48. https://doi.org/10.1145/1989323.1989328
- [8] Daniel Gomez Blanco. 2023. Practical OpenTelemetry. Springer Nature.
- [9] Burton H. Bloom. 1970. Space/Time Trade-Ofs in Hash Coding with Allowable Errors. Commun. ACM 13, 7 (1970), 422–426. https://doi.org/10.1145/362686. 362692
- [10] Peter Boncz, Thomas Neumann, and Orri Erling. 2014. TPC-H Analyzed: Hidden Messages and Lessons Learned from an Infuential Benchmark. In Performance Characterization and Benchmarking. 61–76. https://doi.org/10.1007/978-3-319-04936-6_5
- [11] Peter Boncz, Marcin Zukowski, and Niels Nes. 2005. MonetDB/X100: Hyper-Pipelining Query Execution. In CIDR.
- [12] Martin Burtscher and Paruj Ratanaworabhan. 2007. 高吞吐量双精度浮点数据压缩。发表于Data Compression Conference (DCC). 293–302. https://doi.org/10.1109/DCC.2007.44
- [13] Jef Carpenter and Eben Hewitt. 2016. Cassandra: 权威指南 (第2版)。O'Reilly Media, Inc.
- [14] Bernadette Charron-Bost, Fernando Pedone, and André Schiper (Eds.). 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, and 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, and Wolfgang Lehner. 2019. 从全面的实验调查到轻量级整数压缩算法的基于成本的选择策略。ACM Trans. Database Syst. 44, 3, Article 9 (2019), 46 pages. https://doi.org/10.1145/3323991
- [24] Philippe Dobbelaere and 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.net.cn/docs/Vectorizers.html
- [26] Siying Dong, Andrew Kryczka, Yanqin Jin, and Michael Stumm. 2021. RocksDB:为大规模应用程序提供服务的键值存储开发优先级的演变。ACM Transactions on Storage 17, 4, Article 26 (2021), 32 pages. https://doi.org/10.1145/3483840
- [27] Markus Dreseler, Martin Boissier, Tilmann Rabl, and Matthias Ufacker. 2020. 量化 TPC-H 瓶颈及其优化。Proc. VLDB Endow. 13, 8 (2020), 1206–1220. https://doi.org/10.14778/3389133.3389138
- [28] Ted Dunning. 2021. t-digest:分布的有效估计。Software Impacts 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, and Hasso Plattner. 2016. 使用哈希索引在 SAP HANA 中减少足迹和强制唯一性。发表于Database and Expert Systems Applications. 137–151. https://doi.org/10.1007/978-3-319-44406-2_11
- [30] Philippe Flajolet, Eric Fusy, Olivier Gandouet, and Frederic Meunier. 2007. HyperLogLog:近乎最优的基数估计算法的分析。发表于AofA: Analysis of Algorithms, Vol. DMTCS Proceedings vol. AH, 2007 Conference on Analysis of Algorithms (AofA 07)。Discrete Mathematics and Theoretical Computer Science, 137–156. https://doi.org/10.46298/dmtcs.3545
- [31] Hector Garcia-Molina, Jefrey D. Ullman, and Jennifer Widom. 2009. 数据库系统 - 完整书籍 (第2版)。
- [32] Pawan Goyal, Harrick M. Vin, and Haichen Chen. 1996. 起始时间公平队列:集成服务分组交换网络中的调度算法。26, 4 (1996), 157–168. https://doi.org/10.1145/248157.248171
- [33] Goetz Graefe. 1993. 大型数据库的查询评估技术。ACM Comput. Surv. 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, and Ravi Aringunram. 2018. Pinot:5.3 亿用户的实时 OLAP。发表于2018国际数据管理会议 (SIGMOD '18) (美国德克萨斯州休斯顿)。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, and Matei Zaharia. 2023. 分析和比较湖仓存储系统。CIDR。
- [37] Project Jupyter. 2024. Jupyter Notebooks。检索日期:2024-06-20,网址:https://jupyter.org.cn/
- [38] Timo Kersten, Viktor Leis, Alfons Kemper, Thomas Neumann, Andrew Pavlo, and 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, and Pradeep Dubey. 2010. FAST:现代 CPU 和 GPU 上的快速架构敏感树搜索。发表于2010 ACM SIGMOD 国际数据管理会议 (SIGMOD '10) (美国印第安纳波利斯)。Association for Computing Machinery, New York, NY, USA, 339–350. https://doi.org/10.1145/1807167.1807206
- [40] Donald E. Knuth. 1973. 计算机程序设计艺术,第3卷:排序和搜索。Addison-Wesley。
- [41] André Kohn, Viktor Leis, and 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, and 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, and 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, and Thomas Neumann. 2014. 驱动型并行性:面向多核时代的 NUMA 感知查询评估框架。发表于2014 ACM SIGMOD 国际数据管理会议 (SIGMOD '14) (美国犹他州雪鸟)。Association for Computing Machinery, New York, NY, USA, 743–754. https://doi.org/10.1145/2588555.2610507
- [45] Viktor Leis, Alfons Kemper, and 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, and Brandon Haynes. 2023. 对分析 DBMS 的常见开放格式的深入研究。16, 11 (jul 2023), 3044–3056. https://doi.org/10.14778/3611479.3611507
- [47] 吕正华, 张焕 Hubert, 熊刚, 郭刚, 王浩州, 陈金宝, Asim Praveen, 杨宇, 高晓明, Alexandra Wang, 林文, Ashwin Agrawal, 杨俊峰, 吴浩, 李晓亮, 郭锋, 吴江, 张杰, 和 Venkatesh Raghavan. 2021. Greenplum: 一种用于事务和分析工作负载的混合数据库 (SIGMOD '21). 计算机协会, 纽约, NY, 美国, 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 (2020年8月), 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. 小型的物化聚合:用于数据仓库的轻量级索引结构. 在第二十四届大型数据库国际会议 (VLDB '98) 论文集中. 476–487.
- [52] Jalal Mostafa, Sara Wehbi, Suren Chilingaryan, 和 Andreas Kopmann. 2022. SciTS: 用于科学实验和工业物联网的时间序列数据库的基准测试. 在第三十四届科学和统计数据库管理国际会议 (SSDBM '22) 论文集中. 文章 12. https: //doi.org/10.1145/3538712.3538723
- [53] Thomas Neumann. 2011. 有效地编译用于现代硬件的高效查询计划. Proc. VLDB Endow. 4, 9 (2011年6月), 539–550. https://doi.org/10.14778/ 2002938.2002940
- [54] Thomas Neumann 和 Michael J. Freitag. 2020. Umbra: 一个基于磁盘的系统,具有内存性能. 在第十届创新数据系统研究会议 (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) 中. 计算机协会, 纽约, NY, 美国, 677–689. https://doi.org/10.1145/2723372. 2749436
- [56] LevelDB 在 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 树). Acta Informatica 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 树). Acta Inf. 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 (2022年8月), 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: 一个快速、可扩展的内存时间序列数据库. Proceedings of the VLDB Endowment 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.ac.cn/wiki/Foreign_data_wrappers
- [66] Mark Raasveldt, Pedro Holanda, Tim Gubner, 和 Hannes Mühleisen. 2018. 公平基准测试被认为很困难:数据库性能测试中的常见陷阱. 在测试数据库系统研讨会 (DBTest'18) (美国,休斯顿) 论文集中. 文章 2, 6 页. https://doi.org/10.1145/3209950.3209955
- [67] Mark Raasveldt 和 Hannes Mühleisen. 2019. DuckDB: 一个可嵌入的分析数据库 (SIGMOD '19). 计算机协会, 纽约, NY, 美国, 1981–1984. https://doi.org/10.1145/3299869.3320212
- [68] Jun Rao 和 Kenneth A. Ross. 1999. 缓存感知索引用于内存中的决策支持. 在第二十五届大型数据库国际会议 (VLDB '99) 中. 美国加利福尼亚州旧金山, 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: 一种面向列的 DBMS. 在第三十一届大型数据库国际会议 (VLDB '05) 论文集中. 553–564.
- [72] Teradata. 2024. Teradata 数据库. 检索日期:2024-06-20,来自 https://www. teradata.com/resources/datasheets/teradata-database
- [73] Frederik Transier. 2010. 内存文本搜索引擎的算法和数据结构. 博士论文. 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. 认真对待:基准测试如何未能代表真实世界. 在测试数据库系统研讨会 (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 Rec.
- 29, 3 (2000年9月), 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 国际数据管理会议论文集 (Snowbird, Utah, USA) (SIGMOD '14)。美国纽约,美国计算机协会,157–168. https://doi.org/10.1145/2588555.2595631
- [79] Tianqi Zheng, Zhibin Zhang, 和 Xueqi Cheng. 2020. SAHA: 用于分析数据库的字符串自适应哈希表。Applied Sciences 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