真实EXPLAIN一条普通JOIN看清楚join_algorithm的优先级机制,再对比hash和partial_merge两种算法在已按JOIN key排序的表上的真实表现。
代码CREATE TABLE demo.jleft (k UInt64, v String) ENGINE=MergeTree ORDER BY k; CREATE TABLE demo.jright (k UInt64, name String) ENGINE=MergeTree ORDER BY k; INSERT INTO demo.jleft SELECT number, toString(number) FROM numbers(5000000); INSERT INTO demo.jright SELECT number, concat('name', toString(number)) FROM numbers(1000000); SELECT value FROM system.settings WHERE name='join_algorithm'; EXPLAIN actions=1 SELECT jleft.k, name FROM demo.jleft INNER JOIN demo.jright ON jleft.k=jright.k;
SELECT value FROM system.settings WHERE name='join_algorithm';
→ direct,parallel_hash,hash,ie_join ← 真实是逗号分隔的优先级列表
EXPLAIN actions=1 ... INNER JOIN ...;
→ Join (JOIN FillRightFirst)
Type: inner | Strictness: all
Algorithm: SpillingHashJoin(ConcurrentHashJoin) ← 真实最终选中的算法ClickHouse真实会按这个列表从左到右依次检查每种算法是否适用于当前这条JOIN(比如direct只在右表是Dictionary或者能常数时间查找的特殊引擎时才能用),第一个满足条件的就是真实最终采用的算法——本文这条普通MergeTree表之间的INNER JOIN,真实走到了parallel_hash对应的实现,具体呈现为SpillingHashJoin(ConcurrentHashJoin):一个支持多线程并发构建、并且真实具备落盘能力的哈希JOIN实现。
join_algorithm='hash' :真实内存 89,559,868 字节(约89.5MB),耗时 31ms join_algorithm='partial_merge':真实内存 9,112,463 字节(约9.1MB),耗时 16ms 真实差距:内存少约10倍,耗时快约2倍
每一步都是本文上面用真实EXPLAIN和计时跑出来的原始结果。
EXPLAIN actions=1 与 join_algorithm 设置