bigywater.tech
MySQL / InnoDB · 36

BKA 批量键访问

开启batched_key_access,尝试内表有索引、无索引、强制堵死其它选项三种场景,验证现代MySQL优化器是否还会真的选择BKA策略。

3种场景
全部没能触发BKA
被Hash Join取代
现代优化器更倾向的等值join策略
历史遗留开关
batched_key_access已很少发挥作用

做的事:开启BKA,看它在哪些join场景下会被选中

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

验证:内表有索引时,优化器选的是普通Index lookup,不是BKA

按理说这正是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,不是把这些查找攒批处理。

验证:内表没索引时,优化器跳去选了Hash Join,还是没有BKA

换一张没有索引的内表试试。

结果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。

验证:连禁用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)
做了三种尝试——内表有索引、内表无索引、堵死Hash Join和Block Nested Loop两条退路——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里,但已经更多是历史遗留,而不是还在积极发挥作用的优化路径。

分步演示:追查BKA为什么"消失"了

每一步都是本文上面用EXPLAIN跑出来的原始结果。

三种场景都没能让优化器选中BKA
这一步新验证的场景

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB)
    • optimizer_switch batched_key_access:尝试多种场景验证BKA是否会被选中
    京ICP备2026067502号-1京公网安备11010502063487号