真实用EXPLAIN对比普通JOIN和GLOBAL JOIN的执行计划,看清楚JOIN计算依然发生在各分片本地,GLOBAL关键字真实改变的是右表数据的来源和随之而来的统计信息代价。
代码EXPLAIN actions=1 SELECT count() FROM demo.dist_tbl t INNER JOIN demo.dim_local d ON t.k=d.id; EXPLAIN actions=1 SELECT count() FROM demo.dist_tbl t GLOBAL INNER JOIN demo.dim_local d ON t.k=d.id;
普通JOIN: demo.dim_local[2100] ⋈ demo.local_tbl[1051] Algorithm: SpillingHashJoin(HashJoin) Cost: estimated 1.07 thousand ← 真实有基于本地统计的代价估计 ReadFromMergeTree (demo.dim_local) ← 真实直接读本地磁盘上的表 GLOBAL JOIN: demo.local_tbl ⋈ _data_15869492931996880507_7049575549077029033 Algorithm: SpillingHashJoin(ConcurrentHashJoin) Cost: estimated no stats ← 真实没有代价估计信息 Locality: GLOBAL ← 真实明确标注这是GLOBAL JOIN (右表变成了一个真实的_data_xxx临时表,不是demo.dim_local本身)
两份EXPLAIN真实最关键的区别,不在"JOIN在哪里算"——两种写法的Join算子都真实出现在each分片本地的执行子树里,说明实际的哈希匹配计算依然是在每个分片各自完成的;真实的区别在于JOIN右边喂给这个本地JOIN算子的,到底是哪一份数据——普通JOIN真实直接读demo.dim_local这张表在本分片本地磁盘上的内容;GLOBAL JOIN真实读的是一个名字很怪的临时表_data_xxx,这正是本系列前面《GLOBAL IN和普通IN的区别》验证过的广播机制在JOIN上的真实体现——发起查询的节点先把demo.dim_local算出来打包成这份临时数据,再真实广播给每个分片,各分片本地JOIN算子读的是这份广播来的、内容完全一致的临时数据,而不是各自本地可能不一致的原表。
每一步都是本文上面用真实EXPLAIN跑出来的原始结果。
EXPLAIN actions=1 对比JOIN与GLOBAL JOIN