开启batched_key_access,尝试内表有索引、无索引、强制堵死其它选项三种场景,验证现代MySQL优化器是否还会真的选择BKA策略。
BKA的设计目标是把内表的多次索引查找攒成一批,减少随机访问。
操作SET SESSION optimizer_switch='batched_key_access=on,mrr_cost_based=off'; t18a(5万行) JOIN t18b(20万行,idx_k有索引) ON t18a.k=t18b.k
按理说这正是BKA设计要覆盖的场景。
结果EXPLAIN SELECT t18a.id, t18b.payload FROM t18a JOIN t18b ON t18a.k=t18b.k -> Nested loop inner join -> Table scan on t18a -> Index lookup on t18b using idx_k (k = t18a.k) ← 没有出现"Batched Key Access"字样
哪怕开启了 batched_key_access=on,EXPLAIN里看不到任何"Using join buffer (Batched Key Access)"的字样——优化器选择了普通的逐次Index lookup,不是把这些查找攒批处理。
换一张没有索引的内表试试。
结果EXPLAIN SELECT t18a.id, t18c.payload FROM t18a JOIN t18c ON t18a.k=t18c.k (t18c的k列没有索引) -> Inner hash join (t18c.k = t18a.k) -> Table scan on t18c -> Hash -> Table scan on t18a
没有索引的场景下,优化器选择了Hash Join(下一篇的主角),依然不是BKA。
最后尝试强行堵死其它选项。
结果SET SESSION optimizer_switch='hash_join=off,block_nested_loop=off,batched_key_access=on,mrr_cost_based=off'; EXPLAIN SELECT t18a.id, t18c.payload FROM t18a JOIN t18c ON t18a.k=t18c.k -> Nested loop inner join -> Table scan on t18a -> Filter: (t18c.k = t18a.k) -> Table scan on t18c (退化成了最原始的嵌套循环扫描,依然没有BKA)
batched_key_access 在这台MySQL 26.7实例上一次都没有被选中过。这是一个诚实的发现:BKA这个曾经在MySQL 5.6/5.7时代被作为"join性能优化利器"介绍的特性,在有了更成熟的Hash Join(MySQL 8.0.18引入)之后,的优化器已经很少(或者压根不)选择它了——现代MySQL的等值join场景,交给Hash Join(有可用索引时用Index lookup,没索引时用Hash Join)处理得更好,BKA这个开关虽然还留在optimizer_switch里,但已经更多是历史遗留,而不是还在积极发挥作用的优化路径。
每一步都是本文上面用EXPLAIN跑出来的原始结果。
optimizer_switch batched_key_access:尝试多种场景验证BKA是否会被选中