对比内表无索引场景下,优化器默认选择的Hash Join和强制禁用后退化的裸嵌套循环,看Hash Join把join代价压缩了多少。
上一篇验证了内表无索引时优化器不选BKA,这次专门测Hash Join本身的效果。
建表插入CREATE TABLE t19a (id INT PRIMARY KEY AUTO_INCREMENT, k INT); CREATE TABLE t19b (id INT PRIMARY KEY AUTO_INCREMENT, k INT, payload VARCHAR(50)); 各插入2000行,t19b.k上没有任何索引
这是MySQL 8.0.18之后才有的能力。
结果SET SESSION optimizer_switch='hash_join=on,block_nested_loop=on'; SELECT COUNT(*) FROM t19a JOIN t19b ON t19a.k=t19b.k Hash Join(默认选择): 耗时 0.0009 s, 结果 2000
0.0009秒就返回了正确结果(2000)——Hash Join的做法是:先把较小的一边(t19a)在内存里建成一张哈希表(key是t19a.k),再扫描一遍t19b,对每一行直接用t19b.k去哈希表里做O(1)查找是否匹配,整个过程只需要各扫描一遍两张表,不需要索引。
用optimizer_switch把Hash Join和Block Nested Loop都关掉。
结果SET SESSION optimizer_switch='hash_join=off,block_nested_loop=off'; SELECT COUNT(*) FROM t19a JOIN t19b ON t19a.k=t19b.k 强制Nested Loop(禁用hash join): 耗时 0.3793 s, 结果 2000
同样2000行,同样正确的结果(2000),耗时从0.0009秒暴涨到0.3793秒——差了约421倍。原因是:没有Hash Join也没有Block Nested Loop缓冲优化,退化成最原始的嵌套循环——对t19a的每一行,都要把t19b从头到尾扫一遍去找匹配,总比较次数接近2000×2000=400万次,这是的O(n×m)代价。
每一步都是本文上面用MySQL跑出来的原始耗时。
optimizer_switch hash_join / block_nested_loop:对比Hash Join和裸嵌套循环