首页/数据/SQL 速查/连接驱动顺序与索引配对
性能与执行计划72 条

连接驱动顺序与索引配对

连接把“过滤后结果小”的一侧做驱动、另一侧连接列建索引:这是带来数量级差异的结构性因素。

SQL 速查数据更新于 2026-10-13

驱动侧与被驱动侧的配对

连接的代价主要取决于两件事:先读的那一侧过滤之后产出多少行,以及被读的那一侧能不能用索引按连接键快速定位。前者决定循环次数,后者决定每次循环的代价,两者相乘就是整体规模。很多连接性能问题不是「SQL 写得不对」,而是被驱动侧的连接列上缺一个索引,或者驱动侧本该过滤到几百行却产出几十万行。

基本信息

适用平台MySQL 8.0 / PostgreSQL 12 及以上
驱动侧原则过滤后产出行数越少越好
被驱动侧原则连接列上要有可用索引
多表连接逐级裁剪,每层都要检查被驱动列
哈希连接例外大结果集等值连接时可能不需要索引

典型使用场景

  • 两张表连接出现数量级差异:检查索引是否建在连接列上
  • 多表连接逐层排查:定位是哪一层开始放大的
  • 新语句上线前核对:确认每个连接列都有对应索引
  • 从单表查询改写成连接后性能下降:核对被驱动侧索引
  • 评估可否依赖哈希连接:确认实例版本与数据规模

配对与验证

配对与验证
-- 先给被驱动侧的连接列建索引
ALTER TABLE orders ADD INDEX idx_user_id (user_id);

-- 小结果集在前、大表在后:按连接键逐行查找
SELECT u.name, o.total
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE u.city = 'shanghai';

-- 用计划验证:确认哪张表作为驱动侧,被驱动侧是否用了索引
EXPLAIN SELECT u.name, o.total
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE u.city = 'shanghai';

-- 三张表时逐层检查:每一层的被驱动列都要有索引
-- users → orders → order_items,注意 order_items 的连接列

-- 排序输出列与连接列无关:不要把索引建在输出列上

检查清单

检查项合格标准不合格的表现
驱动侧过滤能力过滤后行数明显小于表总量全表参与循环
被驱动侧索引连接列上有可用索引每次循环都全表扫描
连接条件类型类型一致,避免隐式转换索引因类型不匹配失效
多表层级每层被驱动列都有索引某一层开始行数爆炸
输出列不需要索引把索引建在输出列上
连接列两边的类型要一致:一边是整数列、另一边是字符列时,隐式转换会让索引直接失效,计划里表现为全表扫描,而 SQL 本身看起来毫无问题。
较新版本在大结果集等值连接时会选择哈希连接,此时被驱动侧没有索引也能跑得不错。这种情况下不要机械地加索引,先看计划再决定。

易错点

  • 给输出列建索引:连接性能取决于连接列与过滤列,输出列上的索引基本无用
  • 多表连接只优化第一层:瓶颈往往出现在后面几层的被驱动列上
  • 连接列类型不一致触发隐式转换,索引静默失效,语句看起来毫无问题
  • 用全表扫描的驱动侧去循环查大表:循环次数放大,整体耗时呈数量级上升
  • 只在小数据量的测试库验证:驱动侧行数太少,问题到生产才暴露

数据来源与参考

  • MySQL 8.0 Reference Manual · How MySQL Uses Indexes
  • PostgreSQL Documentation §14.1 Using EXPLAIN
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌