join字段无索引必导致type=all全表扫描,需确保on条件两侧字段均有索引,尤其left join右表必须建索引,复合索引须遵循最左前缀原则,驱动表选择与统计信息准确性同样关键。

JOIN字段没索引,type=ALL是铁律
只要ON条件里任意一侧字段没索引,MySQL就必然走全表扫描——这不是“可能慢”,而是EXPLAIN里直接显示type=ALL。比如SELECT * FROM users u JOIN orders o ON u.id = o.user_id,若orders.user_id没索引,orders表就会被全扫。
实操建议:
- 用
SHOW INDEX FROM users和SHOW INDEX FROM orders确认两边关联字段都有索引 - 外键字段必须显式建索引:
ALTER TABLE orders ADD INDEX idx_user_id (user_id) - 字符串JOIN时,检查
CHARSET和COLLATION是否完全一致,不一致会触发隐式转换,让索引失效
LEFT JOIN右边的表不建索引,等于白写
LEFT JOIN中,左表数据全保留,右表是“被驱动方”,它的扫描效率决定整体性能。只给左表建索引没用,rows值不会降,type仍为ALL。
实操建议:
- 优先确保右表关联字段有索引,例如
LEFT JOIN orders ON users.id = orders.user_id,索引必须建在orders.user_id - 删掉右表索引后立刻复现
type=ALL,这是最直接的验证方式 - 如果业务逻辑强制要用
LEFT JOIN且右表很大,先用子查询缩小左表结果集:SELECT * FROM (SELECT * FROM users WHERE status = 'active') u LEFT JOIN orders o ON u.id = o.user_id
驱动表选错,EXPLAIN里rows爆高
EXPLAIN中rows异常高、key为空或不是预期索引,大概率是优化器把大表当成了驱动表(外层循环),导致小表被反复扫描。
实操建议:
- 用
STRAIGHT_JOIN强制顺序,比如确定users是小表:SELECT * FROM users STRAIGHT_JOIN orders ON users.id = orders.user_id - 对参与JOIN的大表加
WHERE过滤,避免无条件LEFT JOIN——这会让左表全量进入嵌套循环 - 定期运行
ANALYZE TABLE users, orders,让统计信息准确,帮优化器做对选择
复合索引顺序错了,等于没建
联合索引不是“有就行”,列顺序直接影响能否命中。比如ON o.user_id = u.id AND o.status = 'paid',建KEY idx_status_user (status, user_id)就无效,因为不满足最左前缀原则。
实操建议:
- 复合索引按
ON条件从左到右排列,优先放等值匹配字段,再放范围查询字段 - 高频查询只用
status?那就得另建单列索引,或者调整联合索引顺序为(status, user_id) -
EXPLAIN里key_len值能帮你判断用了索引的几列——比如key_len=4说明只用到了第一列
真正卡住性能的,往往不是SQL多复杂,而是索引建在了错误的字段上、顺序颠倒、或漏掉了被驱动表。检查EXPLAIN输出里的type和rows,比调优语句本身更值得花时间。











