多表join性能关键在索引:右表关联字段必须建索引,复合索引需按join字段、等值过滤、范围查询顺序排列,必要时用straight_join强制驱动表,且避免select*及大字段拖累。

WHERE 条件字段没加索引,JOIN 就白搭
多表 JOIN 时,MySQL 不会自动给关联字段建索引。哪怕 ON a.id = b.a_id 写得再规范,只要 b.a_id 没索引,就会触发全表扫描——尤其是 b 表数据量一大,查询直接变慢十倍以上。
实操建议:
- 先用
EXPLAIN看执行计划,重点检查type列是否为ALL或index(说明没走有效索引) -
JOIN的右表(被驱动表)关联字段必须建索引,比如LEFT JOIN orders o ON u.id = o.user_id,o.user_id要有索引 - 左表主键或唯一字段天然适合做驱动条件,但若用非主键字段
JOIN(如u.email),那它也得加索引
复合索引顺序决定 JOIN + WHERE 是否能共用
当查询同时含 JOIN 和 WHERE(比如 WHERE status = 'paid' AND created_at > '2024-01-01'),单列索引往往不够。MySQL 只能用上复合索引的最左前缀,顺序错了,索引就废一半。
举例:想加速 SELECT * FROM users u JOIN orders o ON u.id = o.user_id WHERE o.status = 'paid' AND o.created_at > '2024-01-01',应该建:
ALTER TABLE orders ADD INDEX idx_user_status_created (user_id, status, created_at);
原因:user_id 是 JOIN 条件,放最左;status 是等值过滤,放中间;created_at 是范围查询,放最后——这样三者才能被同一索引覆盖。
常见错误:
- 把
created_at放最左,导致user_id无法走索引 - 只建
(status, user_id),范围条件在前,user_id实际失效
STRAIGHT_JOIN 强制驱动表顺序有时比优化器更靠谱
MySQL 优化器默认选“预估行数少”的表当驱动表,但统计信息过期或数据倾斜时,它可能选错。比如小表 categories 有 10 行,大表 products 有 100 万行,但优化器误判 products 更小,结果先扫百万行再匹配,性能崩盘。
这时可手动指定:
SELECT /*+ STRAIGHT_JOIN */ ... FROM categories c JOIN products p ON p.cat_id = c.id;
或旧版本用:
SELECT STRAIGHT_JOIN ... FROM categories c JOIN products p ON p.cat_id = c.id;
注意点:
- 仅当
EXPLAIN明确显示驱动表不合理时才干预 - 加了
STRAIGHT_JOIN后务必验证执行计划,避免硬编码反而更慢 - 线上变更前,在从库或测试环境压测,因为不同数据分布下最优路径可能不同
避免 SELECT * 和 JSON/TEXT 字段拖垮 JOIN 性能
很多人忽略:即使索引全对、驱动表合理,SELECT * 仍可能让 JOIN 变慢。尤其当某张表含大字段(TEXT、JSON、长 VARCHAR),MySQL 在临时表或排序时需加载整行数据,内存和磁盘 I/O 压力陡增。
实操建议:
- 只查真正需要的字段,例如
SELECT u.name, o.amount, o.status,而不是SELECT * - 如果必须返回大字段,考虑拆成两次查询:先用高效 JOIN 拿主键,再用主键 IN 子查询单独取大字段
- 检查
EXPLAIN中的Extra列,出现Using temporary或Using filesort时,大概率是字段过多或类型不匹配引发的
索引不是建了就生效,关键看它能不能同时覆盖 JOIN 条件、过滤条件和查询字段——漏掉任意一环,性能拐点就藏在看似正确的 SQL 里。










