
本文字数:12318;估计阅读时间:31 分钟
作者: ClickHouse Team
本文在公众号【ClickHouseInc】首发

又到了每月发布新版本的时间!
发布概要
ClickHouse 25.1 版本正式发布,本次更新带来了15项全新功能🦃、36项性能优化⛸️、77个bug修复🏕️
在本次发布中,我们优化了并行哈希连接算法,引入了二级哈希表进行加速;在表级别新增了 MinMax 索引;改进了 Merge 表;增加了自增功能……还有更多精彩更新等你探索!
新贡献者
热烈欢迎 25.1 版本的新贡献者!ClickHouse 社区的成长令人欣喜,我们始终感激每一位贡献者,正是你们的努力让 ClickHouse 变得更加出色和受欢迎。
以下是本次版本的新贡献者名单:
Artem Yurov, Gamezardashvili George, Garrett Thomas, Ivan Nesterov, Jesse Grodman, Jony Mohajan, Juan A. Pedreira, Julian Meyers, Kai Zhu, Manish Gill, Michael Anastasakis, Olli Draese, Pete Hampton, RinChanNOWWW, Sameer Tamsekar, Sante Allegrini, Sergey, Vladimir Zhirov, Yutong Xiao, heymind, jonymohajanGmail, mkalfon, ollidraese
更快的并行哈希连接算法
贡献者:Nikita Taranov
自 24.11 版本以来,并行哈希连接已成为 ClickHouse 的默认连接策略,并且它本身已经是最快的内存哈希表连接算法。但我们从未停止优化,每个版本都会通过底层优化进一步提升连接性能。
在 24.7 版本,我们优化了并行哈希连接的哈希表分配;从 24.12 版本开始,ClickHouse 能够自动判断连接查询中哪个表应用于并行哈希连接的构建阶段。而在 25.1 版本,我们进一步加快了探测阶段的执行速度。
并行哈希连接的执行流程
为了更好地理解这项优化,我们先回顾一下并行哈希连接的构建和探测阶段。下图展示了 ClickHouse 之前的并行哈希连接机制(点击放大):

在 ① 构建阶段,右表的数据被拆分,并由 N 个线程并行处理,分别填充 N 个哈希表实例。N 由 max_threads 控制,本例中设为 4。每个线程执行如下循环:
1. 读取右表的下一批数据块。
2. 对每行的连接键应用 实例哈希函数(图中蓝色),取模线程数,确定目标哈希表实例。
3. 再对连接键应用 插入哈希函数(图中黄色),计算哈希表的存储键,并 ② 插入 该哈希表实例。
4. 继续处理下一批数据块。
在 ③ 探测阶段,左表数据也会被拆分,并由 N 个线程并行处理(同样受 max_threads 控制)。每个线程执行以下循环:
1. 读取左表的下一批数据块。
2. 对连接键应用与构建阶段相同的 实例哈希函数(图中蓝色),取模线程数,确定要查找的哈希表实例。
3. 计算哈希值,在选定的哈希表实例中执行 ④ 查找。
4. 如果查找成功且连接键匹配,则 ⑤ 返回 连接后的结果。
5. 继续处理下一批数据块。
并行哈希连接的构建阶段能够同时填充多个哈希表,从而比传统的非并行哈希连接更快。
传统的非并行哈希连接由于哈希表不支持并发写入,因此只能在单线程中完成插入操作,处理大表时容易成为瓶颈。而在读取阶段,哈希表支持并发查询,因此即便是非并行哈希连接,也能在探测阶段实现高效的并行读取。
相较之下,并行哈希连接的并发构建阶段会在探测阶段带来额外的开销。因为左表的输入数据块需要先拆分,再路由到相应的哈希表实例。
为此,我们对探测阶段进行了优化,使其采用 单个共享哈希表,所有处理线程可以同时访问,就像非并行哈希连接一样。这一改进消除了数据块拆分的需求,减少了额外开销,提高了整体效率。
并行哈希连接优化后的执行流程
下图展示了 ClickHouse 并行哈希连接的优化机制(点击放大):

① 构建阶段 仍然是并发执行的。但是,当 max_threads 设置为 N 时,算法不再并行填充 N 个独立的哈希表实例,而是采用 N 个两级哈希表实例。这些哈希表共有 256 个桶,并由 N 个处理线程并发填充,但填充方式确保不同线程不会操作相同的桶:
-
哈希表实例 #0 负责填充 桶 #0、桶 #N、桶 #(N * 2),…
-
哈希表实例 #1 负责填充 桶 #1、桶 #N + 1、桶 #(N * 2 + 1),…
-
哈希表实例 #2 负责填充 桶 #2、桶 #N + 2、桶 #(N * 2 + 2),…
-
哈希表实例 #3 负责填充 桶 #3、桶 #N + 3、桶 #(N * 2 + 3),…
-
依此类推…
实现这一点的关键在于,每个处理线程执行如下循环:
1. 读取右表的下一批数据块。
2. 计算 插入哈希函数(图中黄色),对 256 取模,确定目标桶编号。
3. 再对桶编号对线程数取模,以确定 目标两级哈希表实例。
4. 使用插入哈希函数的计算结果作为键,将行数据 ② 插入 到选定哈希表实例的对应桶中。
5. 继续处理下一批数据。
这种无重叠的桶填充方式使得

最低0.47元/天 解锁文章
1538

被折叠的 条评论
为什么被折叠?



