一、什么是 BKA(Batched Key Access)?
BKA(Batched Key Access) 是 MySQL 5.6 引入的一种用于优化 JOIN 查询 的执行策略,特别适用于 索引嵌套循环连接(Index Nested Loop Join, INLJ) 场景下的性能提升。
传统 INLJ 在处理外层表(驱动表)的每一行时,都会立即对内层表(被驱动表)发起一次索引查找(lookup)。如果外层表有 N 行,就会产生 N 次随机 I/O 请求。当 N 很大时,这种"逐行访问"模式会导致大量随机 I/O,成为性能瓶颈。
BKA 的核心思想是:将多个键值批量收集后,一次性提交给存储引擎进行查找,从而将多次随机 I/O 转化为更高效的顺序 I/O 或批量 I/O,显著减少磁盘访问次数和 CPU 开销。
二、BKA 的工作原理
1. 执行流程(以 LEFT JOIN 为例)
假设查询:
sql
SELECT * FROM t1 JOIN t2 ON t1.id = t2.t1_id;
其中 t1 是驱动表,t2 是被驱动表,且 t2.t1_id 上有索引。
- 传统 INLJ :遍历
t1的每一行,用t1.id去t2中查匹配行 → N 次索引查找。 - 启用 BKA 后 :
- MySQL 先从
t1读取一批行(batch),比如 1000 行; - 将这些行的
id值收集到一个 MRR(Multi-Range Read)缓冲区 中; - 对这些键值进行 排序(按主键或索引顺序);
- 一次性将排序后的键值范围提交给存储引擎(如 InnoDB);
- 存储引擎利用 MRR 机制 按物理顺序读取数据页,减少随机 I/O;
- 返回结果后,MySQL 再按原始顺序重组结果集。
- MySQL 先从
✅ 关键点 :BKA 依赖于 MRR(Multi-Range Read) 优化。没有 MRR,BKA 无法生效。
三、BKA 的前提条件
要使 BKA 生效,必须同时满足以下条件:
- 优化器选择 Index Nested Loop Join(INLJ) 作为连接算法;
- 被驱动表上有合适的索引(用于等值或范围匹配);
- 启用了 MRR(Multi-Range Read);
- optimizer_switch 中启用了 batched_key_access;
- 使用支持 MRR 的存储引擎(如 InnoDB、MyISAM);
可通过以下命令检查和启用:
sql
-- 查看当前设置
SELECT @@optimizer_switch;
-- 启用 BKA 和 MRR
SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
⚠️ 注意:
mrr_cost_based=off通常建议关闭,因为基于成本的 MRR 判断可能过于保守,导致不启用 MRR/BKA。
四、BKA 的优势
| 优势 | 说明 |
|---|---|
| 减少随机 I/O | 通过批量+排序,将随机访问转为近似顺序访问 |
| 提升缓存命中率 | 数据页连续读取,更利于 Buffer Pool 利用 |
| 降低 CPU 开销 | 减少存储引擎调用次数,合并请求 |
| 适用于大结果集 JOIN | 当驱动表行数多时,收益更明显 |
五、BKA 的局限性与注意事项
- 仅适用于 INLJ:对于 Hash Join(MySQL 8.0+ 支持)或 Block Nested Loop(BNL),BKA 不适用。
- 需要额外内存 :用于缓存 batch 和排序,受
read_rnd_buffer_size和join_buffer_size影响。 - 可能增加延迟:需等待 batch 填满才执行,不适合低延迟要求的 OLTP 场景。
- 排序开销:若键值分布极度离散,排序收益可能被抵消。
- MySQL 8.0+ 中地位下降 :因引入了更高效的 Hash Join,BKA 在等值 JOIN 中使用频率降低,但在非等值 JOIN 或无法使用 Hash Join 时仍有价值。
六、BKA 与相关技术对比
| 技术 | 适用场景 | 是否依赖索引 | 是否批量 | 主要目的 |
|---|---|---|---|---|
| BNL(Block Nested Loop) | 无索引 JOIN | ❌ | ✅(用 join buffer) | 避免重复扫描内表 |
| INLJ(Index Nested Loop) | 有索引 JOIN | ✅ | ❌(逐行) | 利用索引快速查找 |
| BKA + MRR | 有索引 JOIN | ✅ | ✅(批量+排序) | 优化 INLJ 的 I/O |
| Hash Join(MySQL 8.0+) | 等值 JOIN(无论有无索引) | ❌(可选) | ✅ | 构建哈希表,避免嵌套循环 |
💡 在 MySQL 8.0 中,优化器会优先选择 Hash Join(如果条件满足),此时 BKA 自动退居次要地位。
七、总结
- BKA 是对 INLJ 的重要优化,通过批量提交键值并结合 MRR 实现 I/O 优化;
- 核心依赖 MRR ,需手动开启(因默认
mrr_cost_based=on可能禁用); - 适用于驱动表较大、被驱动表有索引的 JOIN 场景;
- 在 MySQL 8.0+ 中重要性下降,但仍是理解查询优化演进的关键知识点;
- 合理配置
read_rnd_buffer_size和join_buffer_size可提升 BKA 效果。
八、面试问题与参考答案
问题 1:
"请解释 MySQL 中 BKA(Batched Key Access)的作用和工作原理,并说明它依赖哪些前提条件?"
参考答案:
BKA 是 MySQL 5.6 引入的一种用于优化 Index Nested Loop Join(INLJ)的机制。传统 INLJ 对驱动表的每一行都立即发起一次索引查找,导致大量随机 I/O。BKA 通过将多个键值批量收集、排序后一次性提交给存储引擎,利用 MRR(Multi-Range Read)机制将随机 I/O 转为近似顺序 I/O,从而提升性能。
BKA 生效需满足以下条件:
- 优化器选择 INLJ 作为连接算法;
- 被驱动表上有可用索引;
- 启用了 MRR(
mrr=on且建议mrr_cost_based=off); optimizer_switch中启用了batched_key_access=on;- 使用支持 MRR 的存储引擎(如 InnoDB)。
问题 2:
"在 MySQL 8.0 中,BKA 是否仍然重要?为什么?"
参考答案:
在 MySQL 8.0 中,BKA 的重要性有所下降,主要原因是在 8.0.18 版本引入了 Hash Join。Hash Join 对于等值连接(equi-join)无论是否有索引都能高效执行,且通常比 INLJ(包括 BKA 优化版)更快。因此,优化器在满足条件时会优先选择 Hash Join,而不再使用 INLJ + BKA。
不过,BKA 仍有其价值:
- 在 非等值连接 (如
<,BETWEEN)中,Hash Join 无法使用,此时 INLJ + BKA 仍是优选; - 当 内存不足无法构建哈希表 时,优化器可能回退到 INLJ;