直接回答:MySQL 的 JOIN 以嵌套循环家族为主:Simple Nested-Loop(外表每行都全扫内表,复杂度 O(M×N),最差)、Index Nested-Loop(内表走索引,最常见的好情况)、Block Nested-Loop(外表一批行缓存进 join_buffer,内表只扫一遍做块匹配,用于内表无索引时);MySQL 8.0.18 引入 Hash Join,对等值且无索引可用的 JOIN 构建哈希表,8.0.20 起完全替代 BNL。
展开解析:EXPLAIN 的 Extra 列出现 “Using join buffer (Block Nested Loop)” 或 “hash join” 说明内表没走索引,这是优化信号。Hash Join 在内存构建驱动表的哈希表,内表扫描时做 O(1) 探测,内存不足时分批溢写磁盘(grace hash join);对百万行级无索引关联比 BNL 快一个数量级,是 8.0 最重要的查询优化之一。还有 BKA(Batched Key Access)配合 MRR 把内表索引访问排序成近似顺序 I/O,默认关闭需手动开启 optimizer_switch。优化核心:被驱动表(内表)的连接列必须有索引;用小结果集做驱动表减少外层循环次数;join_buffer_size 足够装下驱动表批次。常见坑是连接列类型或字符集不一致(int 对 varchar、utf8 对 utf8mb4)触发隐式转换导致索引失效。多表 JOIN 时用 EXPLAIN 看 rows 和驱动顺序,8.0 可用 EXPLAIN ANALYZE 拿到真实执行耗时和行数,必要时用 STRAIGHT_JOIN 固定顺序验证;避免 SELECT * 放大 join buffer 占用。
EXPLAIN SELECT o.id FROM orders o JOIN users u ON o.user_id = u.id;
-- u.id 有索引 => Index Nested-Loop;否则 Extra 显示 join buffer / hash join
追问方向:优化器怎么选驱动表?Hash Join 与 BNL 各自适用什么条件?t1 小表 join t2 大表一定小表做驱动表吗? (约 353 字)