ClickHouse 26.2 版本发布说明
本文字数:11205;估计阅读时间:29分钟
作者:ClickHouse Team
Meetup活动
ClickHouse 深圳第3届 Meetup 火热报名中,详见文末海报!
又一个月过去了,这意味着又到了发布新版本的时候了!
ClickHouse 冬季版本带来了 25 项新功能 🧤、43 项性能优化 🛷 和 183 项错误修复 ⛄。
本次发布中,text-index 和 QBit data type 已达到生产就绪状态。此外,现在可以按时间批量处理“无限”插入,并且对 joins、JSON 解析以及带有 min-max indices 的插入操作进行了性能改进。
热烈欢迎所有参与 26.2 版本的全新贡献者!ClickHouse 社区的蓬勃发展令我们深感敬佩,我们始终对这些让 ClickHouse 如此受欢迎的贡献致以最诚挚的感谢。
以下是新贡献者的名单:
4ertus2,Aaron Knudtson,AlyHKafoury,Andre Hora,Andrey Tarasov,Ashwath,Ben Wu,Christoph Viebig,Dan McCombs,Dmitry Kovalev,Dmitry Plotnikov,Federico Ginosa,Gerald Latkovic,Hasyimi Bahrudin,Ivan Gorin,Kien Nguyen Tuan,Mostafa Mohamed Salah,MyeongjunKim,Padraic Slattery,Rahul,Raquel Barbadillo,Visakh Unnikrishnan,daun-gatal,dimbo4ka,dk-github,jayvenn21,murphy-4o,phulv94,sunyeongchoi,vanchaklar,Álvaro Niño
提示:如果您对我们如何生成此列表感到好奇……请点击这里(https://gist.github.com/gingerwizard/5a9a87a39ba93b422d8640d811e269e9)。
您也可以查看本次发布的演示文稿幻灯片(https://presentations.clickhouse.com/2026-release-26.2/)。
贡献者:Mostafa Mohamed Salah
我最喜欢的实时数据集之一是 Wikimedia 最近更改数据流 (Wikimedia recent changes feed),它实时传输 Wikimedia 各项资产的变更信息。
您可以通过访问 https://stream.wikimedia.org/v2/stream/recentchange 来查看其工作原理。一个事件示例如下所示:
event: messageid: [{"topic":"eqiad.mediawiki.recentchange","partition":0,"offset":-1},{"topic":"codfw.mediawiki.recentchange","partition":0,"timestamp":1772536525049}]data: {",[object Object],":"/mediawiki/recentchange/1.0.0","meta":{"uri":"https://commons.wikimedia.org/wiki/Category:Taken_with_Nikon_D3100","request_id":"55711a7f-6053-4592-97c4-45d54e6319f7","id":"9106b913-64f9-4dff-a325-ac43492cc81d","domain":"commons.wikimedia.org","stream":"mediawiki.recentchange","dt":"2026-03-03T11:15:25.048Z","topic":"codfw.mediawiki.recentchange","partition":0,"offset":2029032842},"id":3219900521,"type":"categorize","namespace":14,"title":"Category:Taken with Nikon D3100","title_url":"https://commons.wikimedia.org/wiki/Category:Taken_with_Nikon_D3100","comment":"[[:File:Ferrari 550 Maranello - Flickr - Alexandre Prévot (3).jpg]] added to category","timestamp":1772536523,"user":"Rkieferbot","bot":true,"notify_url":"https://commons.wikimedia.org/w/index.php?diff=1175166536&oldid=1019467923&rcid=3219900521","server_url":"https://commons.wikimedia.org","server_name":"commons.wikimedia.org","server_script_path":"/w","wiki":"commonswiki","parsedcomment":"File:Ferrari 550 Maranello - Flickr - Alexandre Prévot (3).jpg added to category"}
每个事件包含三个属性;
• event - 事件类型,通常为 message 。
• id - 事件的标识符。
• data - 一个表示变更内容的 JSON 对象。
我们可以在终端使用 cURL,仅流式传输每个事件的 data 部分:
curl -sS --globoff \
-H 'Accept: application/json' \
--no-buffer \
"https://stream.wikimedia.org/v2/stream/recentchange"
要将此数据加载到 ClickHouse 中,我们首先需要创建一个表。虽然我们可以将数据拆分成单独的列,但这是一个使用 JSON 数据类型的好时机:
CREATE table wiki (
json JSON
);
接着,我们可以更新 cURL 命令来流式导入数据:
curl -sS --globoff \
-H 'Accept: application/json' \
--no-buffer \
"https://stream.wikimedia.org/v2/stream/recentchange" |
./clickhouse client --query="INSERT INTO wiki FORMAT JSONAsObject"
如果我们打开另一个标签页并连接到我们的 ClickHouse 服务器 (ClickHouse Server),我们会发现没有任何数据被导入。这是因为 min_insert_block_size_rows 和 min_insert_block_size_bytes 的默认值分别为 1,000,000 和 268 MB。Wikimedia 变更数据集每秒仅生成 10 行数据,因此我们将需要等待相当长的时间!
我们可以将这些参数设置为较低的值来解决这个问题,具体操作如以下视频所示:
这种方法虽然有效,但我们无法得知数据何时会被刷新到表中。ClickHouse 26.2 引入了一个新设置 input_format_max_block_wait_ms,它允许用户根据时间而非大小来定义块的刷新间隔。此设置仅在与 input_format_connection_handling 结合使用时才有效,该机制能够确保即使连接意外关闭,缓冲区中任何剩余数据也能被解析和处理,而不会被当作错误处理。
因此,如果想每 3 秒摄取一次数据,我们可以将数据摄取代码更新为如下所示:
curl -sS --globoff \
-H 'Accept: application/json' \
--no-buffer \
"https://stream.wikimedia.org/v2/stream/recentchange" |
./clickhouse client --query="INSERT INTO wiki FORMAT JSONAsObject" --min_insert_block_size_rows=0 \
--min_insert_block_size_bytes=0 \
--input_format_max_block_wait_ms 3000 \
--input_format_connection_handling 1
在另一个标签页中,我们可以检查已摄入的记录数量,在每次查询执行后休眠一秒:
while true; do
./clickhouse client "SELECT now(), count() FROM wiki FORMAT TabSeparated"
sleep 1
done
我们将看到以下输出,其中计数大约每三行更新一次:
2026-03-03 11:47:11 13898
2026-03-03 11:47:12 13898
2026-03-03 11:47:13 14008
2026-03-03 11:47:14 14008
2026-03-03 11:47:15 14008
2026-03-03 11:47:17 14128
2026-03-03 11:47:18 14128
2026-03-03 11:47:19 14128
2026-03-03 11:47:20 14213
下面的动画演示了 input_format_max_block_wait_ms 设置的工作原理。此设置定义了服务器在处理传入数据时构建的内存块 (in-memory blocks) 的基于时间的刷新间隔。当超时到期时,当前数据块 (block) 会被写入一个新的数据分区 (data part),从而使插入的行对查询可见。
ClickStack 是一个可观测性平台 (observability platform),它将日志 (logs)、追踪 (traces)、指标 (metrics) 和会话 (sessions) 统一到一个高性能解决方案中。它由 ClickHouse、OpenTelemetry (OpenTelemetry) 以及 ClickStack UI (ClickStack UI) (前身为 HyperDX (HyperDX)) 组成。
在 ClickHouse 26.2 之前,如果您想使用 ClickStack,有两种选择:启动 Docker 容器或使用 Managed ClickStack。
自 ClickHouse 26.2 起,我们推出了一种新的分发方式:内置于 ClickHouse 中的 ClickStack UI (User Interface)。ClickStack UI 现已直接分发并嵌入到 ClickHouse 二进制文件中,让您比以往任何时候都能更轻松地使用 ClickHouse 探索可观测性数据。只需导航至 https://localhost,选择“ClickStack”,即可开始探索。
您可以在《Introducing ClickStack embedded in ClickHouse》中阅读更多详情(https://clickhouse.com/blog/clickstack-embedded-clickhouse)。
该文本索引自 2022 年起一直在开发中,于 ClickHouse 25.9 达到实验性状态,并在 ClickHouse 25.12 进入 Beta 版。自 ClickHouse 26.2 起,文本索引已达到生产就绪状态,欢迎大家试用并反馈使用体验。
与文本索引一同达到生产就绪状态的,还有用于向量嵌入的 QBit 数据类型,它支持搜索精度的运行时调优。QBit 在 ClickHouse 25.10 中引入,并在 26.1 中提升为 Beta 版,现已于 26.2 全面生产就绪。
由 Nihal Miaji 贡献
ClickHouse 26.2 版本还引入了一个新的 system.primes 表。顾名思义,该表返回素数。
以下查询返回前十个素数、这些素数的总和以及第十个素数:
SELECT groupArray(prime), max(prime), sum(prime)
FROM primes(10);
Row 1:
──────
groupArray(prime): [2,3,5,7,11,13,17,19,23,29]
max(prime): 29
sum(prime): 129
此函数性能极佳。以下查询计算前 10 亿个素数的最小值、最大值和总和:
SELECT min(prime), max(prime), sum(prime)
FROM primes(1000000000);
┌─min(prime)─┬──max(prime)─┬───────────sum(prime)─┐
│ 2 │ 22801763489 │ 11138479445180240497 │
└────────────┴─────────────┴──────────────────────┘
1 row in set. Elapsed: 36.444 sec. Processed 1.00 billion rows, 8.00 GB (27.44 million rows/s., 219.51 MB/s.)
Peak memory usage: 348.15 KiB.
而它仅耗时 36 秒多!🤯
尽管圆周率日已过去数日,但您是否知道欧拉对巴塞尔问题的解法将素数与 π 连接起来?欧拉证明了 ∑ 1/n² = π²/6,这也可以表达为所有素数的无限乘积。我们可以使用 ClickHouse 的 primes 表函数来近似计算此值:
SELECT sqrt(6 * exp(sum(log(pow(prime, 2) / (pow(prime, 2) - 1)))))
FROM primes(10000000)
┌─sqrt(multipl⋯), 1)))))))─┐
│ 3.141592653079655 │
└──────────────────────────┘
1 row in set. Elapsed: 0.415 sec. Processed 10.00 million rows, 80.00 MB (24.10 million rows/s., 192.78 MB/s.)
Peak memory usage: 141.38 KiB.
由 Yarik Briukhovetskyi 贡献
本次发布提升了 ClickHouse 支持的众多 连接类型 中的两种——RIGHT OUTER JOIN 和 FULL OUTER JOIN 的性能。
提醒一下,当左表和右表进行连接时:
SELECT...
FROM left_table JOIN right_table ON ...
RIGHT OUTER JOIN 也会返回右表中在左侧没有匹配的行,并用默认值填充左表的相应列。
FULL OUTER JOIN 返回来自两个表的未匹配行,并用默认值填充相应侧的缺失列。
与内连接(Inner Join)或左连接(Left Join)相比,这些连接类型需要额外的工作。因此,为了理解本次发布中的优化,我们首先需要了解 ClickHouse 内部如何执行连接。
ClickHouse 中的连接管道
ClickHouse 默认使用 并行哈希连接算法 执行连接,其物理查询计划(即“查询管道”)如下图所示。
① 右表被划分为 N 个桶,由 N 个线程并行处理(N = max_threads,默认为 CPU 核数,本例中为 2),每个桶都会构建一个内存哈希表。
② 左表也以相同方式分区并并行处理,从而使匹配的行能够到达相应的哈希表。
③ 通过探测匹配的哈希表进行行连接,从而产生最终结果。
考虑到这种执行模型,不同 OUTER JOIN 类型的行为就更容易理解了。
为什么 LEFT OUTER JOIN 开销较小
在 LEFT OUTER JOIN 中,未匹配的行在管道中是天然存在的。
左表的所有行都经过流式传输 (②) 并与哈希表进行探测 (③)。如果未找到匹配项,该行可以立即填充右表列的默认值并输出。
为什么 RIGHT / FULL OUTER JOIN 更具挑战性
对于 RIGHT OUTER JOIN 和 FULL OUTER JOIN,情况则有所不同。
这些连接还必须返回右表中从未匹配左侧任何行的行。
但右表在构建哈希表时 (①) 已经提前被处理,因此这些行在主管道流中不再可见。
为了产生最终结果,ClickHouse 必须遍历右表数据,并生成那些从未匹配的行。
非匹配行并行生成 (26.2)
此前,此后处理步骤以单线程模式运行。
自 26.2 版本起,右表中的非匹配行将并行生成,每个右表数据桶使用一个线程。
此行为由新增的 parallel_non_joined_rows_processing 设置控制(该设置默认启用)。
性能提升
为阐明其影响,我们使用匿名化网络分析数据集进行演示,该数据集加载在一个由 gp3 EBS 卷支持的 AWS m6i.8xlarge 实例(32 核、128 GB RAM)上。
以下查询执行 FULL OUTER 自连接,统计用户导航步骤,包括页面间跳转以及访问入口和出口。
SELECT count()
FROM hits AS t1
FULL JOIN hits AS t2
ON t1.URL = t2.Referer
AND t1.UserID = t2.UserID
AND t1.URL != ''
AND t2.Referer != '';
首先,我们运行相同的查询三次,并将 parallel_non_joined_rows_processing 设置为 0,以重现先前版本的行为。
以下是 clickhouse-client 打印的执行统计信息:
1 row in set. Elapsed: 35.367 sec. Processed 199.99 million rows, 17.91 GB (5.65 million rows/s., 506.30 MB/s.)
Peak memory usage: 19.53 GiB.
1 row in set. Elapsed: 35.128 sec. Processed 199.99 million rows, 17.91 GB (5.69 million rows/s., 509.74 MB/s.)
Peak memory usage: 19.53 GiB.
1 row in set. Elapsed: 35.538 sec. Processed 199.99 million rows, 17.91 GB (5.63 million rows/s., 503.86 MB/s.)
Peak memory usage: 19.53 GiB.
接下来,我们运行相同的查询三次,并将 parallel_non_joined_rows_processing 设置为 1(默认设置):
1 row in set. Elapsed: 11.226 sec. Processed 299.98 million rows, 18.11 GB (26.72 million rows/s., 1.61 GB/s.)
Peak memory usage: 19.66 GiB.
1 row in set. Elapsed: 11.133 sec. Processed 299.98 million rows, 18.11 GB (26.94 million rows/s., 1.63 GB/s.)
Peak memory usage: 19.64 GiB.
1 row in set. Elapsed: 11.210 sec. Processed 299.98 million rows, 18.11 GB (26.76 million rows/s., 1.62 GB/s.)
Peak memory usage: 19.67 GiB.
这实现了 3.2 倍的加速,将运行时间从约 35 秒缩短至约 11 秒。
由 Pavel Kruglov 和 Raúl Marín 贡献
OUTER JOIN 并不是唯一获得性能提升的功能。
此版本还包括针对 JSON 解析、uniq 计算以及 minmax 索引创建方面的优化。
更快的 JSON 解析
JSON 数据类型的解析已得到优化。
该 PR 测试截图中的每条柱状图都对比了新旧性能,显示出大约 1.2 倍至 2.8 倍的加速效果。
更快的 uniq 计算
对于不带 GROUP BY 子句的查询,针对数值类型的 uniq 操作现在在可能时会批量插入,从而减少 CPU 开销并提升性能。
该 PR 的性能测试显示出持续的改进,其速度提升约 1.15 倍。
使用 minmax 索引提升 INSERT 速度
在 INSERT 操作期间,minmax 索引的计算现在效率更高,它移除了不必要的数据复制,并对数值列采用了向量化的最小/最大值计算。当存在大量索引列时,这会降低插入延迟。
该 PR 的性能测试表明,向包含 minmax 索引的表中插入数据的速度提升了约 1.2 倍。
说起 minmax 索引,本次发布也使其更易于使用。
由 Michael Jarrett 贡献
本次发布引入了一种更简便的方式,可以在表级别自动为时间列启用 minmax 索引。
Minmax 索引是 ClickHouse 用于对过滤索引列的查询进行早期数据剪枝(prune data)的关键机制之一,它与稀疏主索引(sparse primary index)和轻量级投影(lightweight projections)一同发挥作用。
在查看语法之前,让我们简要回顾一下 ClickHouse 中的剪枝机制如何运作,以及 minmax 索引在其中所处的位置。
快速回顾:ClickHouse 中的剪枝
最快的分析查询,其秘诀在于读取最少的数据。
分析型工作负载通常会对连续的行范围进行筛选,然后聚合结果。因此,性能高低取决于能否尽可能多地跳过不必要的数据。
ClickHouse 通过以下方式实现这一点:将数据按主键排序(如下图中的 C1 所示),并将行组织成数据粒(granule)(g1–g4)。数据粒是 ClickHouse 中最小的处理单元,每个数据粒默认包含 8,192 行(为清晰起见,图中仅显示每个数据粒包含 3 行)。
基于这种数据粒组织方式,ClickHouse 可以运用不同的剪枝技术,从而在对索引列进行过滤的查询中跳过整个数据粒:
① 主索引
主索引 (①) 存储每个数据粒的第一个主键列值。根据主键上的过滤条件,主索引允许在读取数据粒之前跳过整个数据粒。
例如,对于 WHERE C1 > 60,数据粒 g1 和 g2 通过索引被剪枝,因此只读取剩余部分的数据。
② 轻量级投影
对于针对非主键列的过滤器,例如 WHERE C2 > 900,ClickHouse 可以使用 轻量级投影 (lightweight projection)。这种投影存储已排序的投影键 (projection key) (C2) 值以及 _part_offset 值,并提供自身的主索引 (primary index) (②),通过该索引,可以根据投影键上的过滤器裁剪粒度 (granule)。
③ 最小最大索引 (Minmax indexes)
然而,即使是轻量级投影,仍然会在磁盘上重复存储投影键列的值。
如果过滤列(例如 C3)与主键的顺序存在关联,ClickHouse 可以利用最小最大索引 (minmax index) 而非投影来裁剪粒度。
在上方的图表中,最小最大索引 (③) 记录了每个粒度的 C3 列的最小值和最大值。
对于 WHERE C3 > 600 这样的过滤器,粒度 g1-g3 可以跳过,由于其最大值均小于 600,因此只需读取 g4 即可。
相同的最小最大元数据也可以 加速 Top-N 查询,例如 SELECT * FROM T ORDER BY C3 DESC LIMIT 3,从而使 ClickHouse 能够跳过不会影响最终结果的粒度。
自动启用最小最大索引
由于最小最大索引极为实用,ClickHouse 提供了一种便捷的方式,可以为一整类列自动启用它们,而无需为每列手动定义索引。
25.1 版本 引入了 MergeTree 表设置: •add_minmax_index_for_numeric_columns •add_minmax_index_for_string_columns
26.2 版本通过以下设置将其扩展到时间列 (Date / DateTime / Time 类型): • add_minmax_index_for_temporal_columns
示例如下:
CREATE TABLE pageviews (
event_time DateTime,
...
)
SETTINGS add_minmax_index_for_temporal_columns = 1;
由于 event_time 是时间列,在该设置启用后,ClickHouse 会自动为其创建最小最大索引,无需显式定义索引。
这种方法有以下几个优点:
• 无需考虑哪些列需要拥有最小最大索引
• 无需为每列手动定义索引
• 最小最大索引占用空间小,且仅在查询对相应列进行过滤时才加载,因此它们带来的开销很小,但在必要时能显著加快查询速度。
贡献者:Denis Kamenskii, Vladimir Cherkasov
您现在可以在 clickhouse-client 中进行安全的交互式认证,支持 Google Authenticator、1Password、Okta 及类似工具。
您首先需要生成一个支持 TOTP (基于时间的一次性密码) 的密钥:
base32 -w32 < /dev/urandom | head -1
5RN2JMUDXJARFMPUYKXGH3N35DPGRCSU
接下来,您可以利用该密钥生成一个二维码,以便使用您的认证器应用进行扫描:
qrencode -t ansiutf8 'otpauth://totp/ClickHouse?issuer=ClickHouse&secret=5RN2JMUDXJARFMPUYKXGH3N35DPGRCSU'
这将生成一个二维码,您可以使用认证器应用扫描它。
接下来,我们将在 ClickHouse 中配置用户:
config.d/users.yaml
users:
totp_user:
password_sha256_hex: 1464acd6765f91fccd3f5bf4f14ebb7ca69f53af91b0a5790c2bba9d8819417b
time_based_one_time_password:
secret: 5RN2JMUDXJARFMPUYKXGH3N35DPGRCSU
period: 30
digits: 6
algorithm: SHA1
networks:
ip: '::/0'
profile: default
quota: default
之后,我们就可以使用刚刚创建的用户连接到 ClickHouse:
./clickhouse client --user totp_user
系统将提示您输入密码,随后是您的认证器应用中生成的 TOTP 码。
好消息:ClickHouse Shenzhen User Group第 3 届 Meetup 火热报名中,将于2026年3月28日在深圳市南山区粤海街道桑达科技大厦1楼蓝马咖啡 举行,扫码免费报名
/END/
试用阿里云 ClickHouse企业版
轻松节省30%云资源成本?阿里云数据库ClickHouse 云原生架构全新升级,首次购买ClickHouse企业版计算和存储资源组合,首月消费不超过99.58元(包含最大16CCU+450G OSS用量)了解详情:https://t.aliyun.com/Kz5Z0q9G
征稿启示
面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:[email protected]