分布式Join通常需要跨节点Shuffle数据,当表规模增大时Shuffle开销会成为性能瓶颈。Colocate Join通过将频繁关联的表按相同规则分布到相同节点,使Join在本地完成,完全避免网络传输。本文介绍如何创建和管理Colocate Group,以及如何验证Colocate Join是否生效。
Colocate Join原理
常规分布式Join需要将两张表的数据按Join Key重新Hash分发到相同节点,这个Shuffle过程涉及大量网络传输,在大表场景下往往是查询的性能瓶颈。
Colocate Join的思路是从建表阶段就解决数据分布问题:如果两张表按相同的分桶键和分桶数进行Hash分布,那么相同Key值的数据天然在同一个BE节点上,Join时直接本地执行即可。
SelectDB通过Colocate Group管理这种数据共置关系。同一Group内的表保证:
-
相同Hash值的Bucket分布在相同的BE节点。
-
节点变更时,组内所有表的Bucket映射统一调整。
前提条件
同一Colocate Group内的表必须满足以下条件,否则建表或修改会报错:
|
约束项 |
要求 |
说明 |
|
分桶键 |
数据类型和数量一致 |
列名可以不同,但数据类型必须完全相同(如不能一张表用INT另一张用BIGINT) |
|
分桶数量 |
完全一致 |
所有表的BUCKETS数必须相同,不能使用 |
不满足条件时建表会报错,例如分桶键类型不匹配时提示Colocate tables distribution columns must have the same data type。
创建Colocate表
建表时通过colocate_with属性指定Group名称,同名即同组:
-- 订单表,以user_id分桶,加入group_user
CREATE TABLE orders (
order_id BIGINT,
user_id BIGINT,
product_id BIGINT,
order_date DATE,
amount DECIMAL(10,2)
) DISTRIBUTED BY HASH(user_id) BUCKETS 4
PROPERTIES (
"colocate_with" = "group_user"
);
-- 用户表,同样以user_id分桶,加入同一Group
CREATE TABLE users (
user_id BIGINT,
username VARCHAR(100),
city_id BIGINT,
vip_level INT
) DISTRIBUTED BY HASH(user_id) BUCKETS 4
PROPERTIES (
"colocate_with" = "group_user"
);
两张表指定相同的colocate_with值即属于同一Colocate Group。首张表创建时Group自动建立,后续表加入时系统检查约束是否满足。
查看Colocate Group状态
通过SHOW PROC '/colocation_group'查看当前所有Group信息:
SHOW PROC '/colocation_group';
输出示例:
+-------------------------------+----------------------------+-----------+------------+-------------------+---------+----------+----------+
| GroupId | GroupName | TableIds | BucketsNum | ReplicaAllocation | DistCols| IsStable | ErrorMsg |
+-------------------------------+----------------------------+-----------+------------+-------------------+---------+----------+----------+
| 1778568404439.1778568404759 | test_query_opt.group_user | 10749,10776| 4 | tag.location.default:1| bigint | true | |
+-------------------------------+----------------------------+-----------+------------+-------------------+---------+----------+----------+
关键字段说明:
-
GroupName:格式为{数据库名}.{Group名称}。 -
TableIds:Group内的表ID列表。 -
BucketsNum:组内统一的分桶数。 -
IsStable:true表示数据分布已平衡,Colocate Join可正常使用;false表示正在重平衡(通常因BE节点增减触发),此时Colocate Join会退化为Shuffle Join。
验证Colocate Join生效
当Join的两张表属于同一Colocate Group且Join Key为分桶键时,优化器自动选择Colocate Join。通过EXPLAIN确认:
EXPLAIN SELECT o.order_id, u.username, o.amount
FROM orders o JOIN users u ON o.user_id = u.user_id
WHERE o.order_date >= '2024-01-01';
-- 在输出中查找join op字段:
-- join op: INNER JOIN(COLOCATE[])[] 表示Colocate Join已生效
-- 如果显示 INNER JOIN(PARTITIONED) 或 INNER JOIN(BROADCAST),说明退化了
以下情况Colocate Join会退化为Shuffle或Broadcast:
-
Join Key不是分桶键。例如orders和users虽在同一Group,但如果按product_id关联则无法使用Colocate。
-
Group的
IsStable为false,数据正在重平衡。 -
通过Session变量手动关闭了Colocate计划:
SET disable_colocate_plan = true。
管理Colocate Group
通过ALTER TABLE可以随时调整表的Group归属:
-- 将表加入一个Group(Group不存在则自动创建)
ALTER TABLE orders SET ("colocate_with" = "group_user");
-- 将表从一个Group移到另一个Group
ALTER TABLE orders SET ("colocate_with" = "group_new");
-- 将表移出Colocate Group
ALTER TABLE orders SET ("colocate_with" = "");
修改colocate_with时系统会检查目标Group的约束(分桶键类型、分桶数),不满足则报错。
删除Group内的表时,表先进入回收站。当Group内最后一张表从回收站彻底删除后,Group才自动清除。即使Group内所有表都已删除,在彻底清除前Group的Schema信息(分桶键类型等)仍保留,新加入的表仍需满足原有约束。
典型场景
星型模型中事实表与维度表Join
将事实表与高频关联的维度表放入同一Colocate Group,以关联键作为分桶键:
-- orders和users均以user_id分桶,Join走Colocate无需Shuffle
SELECT u.username, SUM(o.amount) AS total_sales
FROM orders o
JOIN users u ON o.user_id = u.user_id
WHERE o.order_date BETWEEN '2024-01-01' AND '2024-06-30'
GROUP BY u.username;
Colocate Join只在Join Key等于分桶键时触发。如果还需要按product_id关联products表,该Join不走Colocate。对于多条高频Join路径,需要为不同的Join Key分别创建Colocate Group。
实时数仓多表关联
实时导入的多张明细表如果经常按用户维度关联分析,将它们统一按用户ID分桶并加入同一Group,可避免大规模数据的网络Shuffle。
使用建议
-
分桶键应选频繁Join的关联列,而非分区列。
-
合理规划分桶数量。过少导致数据倾斜,过多增加元数据开销,建议单个Bucket数据量在100MB~1GB之间。
-
同一Group内表数量不宜过多,建议不超过10张。
-
节点扩缩容后关注
IsStable状态,待恢复true后再执行性能敏感的查询。