图片

本文字数:11205;估计阅读时间:29分钟

作者:ClickHouse Team

本文在公众号【ClickHouseInc】首发

图片

又一个月过去了,这意味着又到了发布新版本的时候了!

ClickHouse 冬季版本带来了 25 项新功能 🧤、43 项性能优化 🛷 和 183 项错误修复 ⛄。

本次发布中,text-index 和 QBit data type 已达到生产就绪状态。此外,现在可以按时间批量处理“无限”插入,并且对 joinsJSON 解析以及带有 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: message
id: [{"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 (ClickStack)

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)。

生产就绪:文本索引与 QBit 数据类型

该文本索引自 2022 年起一直在开发中,于 ClickHouse 25.9 达到实验性状态,并在 ClickHouse 25.12 进入 Beta 版。自 ClickHouse 26.2 起,文本索引已达到生产就绪状态,欢迎大家试用并反馈使用体验。

与文本索引一同达到生产就绪状态的,还有用于向量嵌入的 QBit 数据类型,它支持搜索精度的运行时调优。QBit 在 ClickHouse 25.10 中引入,并在 26.1 中提升为 Beta 版,现已于 26.2 全面生产就绪。

表函数 primes

由 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.

更快的 RIGHT 和 FULL JOIN

由 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 索引,本次发布也使其更易于使用。

自动启用 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 会自动为其创建最小最大索引,无需显式定义索引。

这种方法有以下几个优点:

• 无需考虑哪些列需要拥有最小最大索引

• 无需为每列手动定义索引

• 最小最大索引占用空间小,且仅在查询对相应列进行过滤时才加载,因此它们带来的开销很小,但在必要时能显著加快查询速度。

基于时间的动态口令 (Time-Based One-Time Passwords)

贡献者: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 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐