关联字段无索引会导致join退化为全表扫描;必须为on字段建索引,注意类型一致、避免隐式转换,复合索引需按等值→范围→排序顺序设计,并确保驱动表筛选性高。

关联字段没索引,JOIN基本就慢成全表扫描
只要 ON 子句里用的字段没索引,绝大多数数据库(MySQL/PostgreSQL/SQL Server)都会退化为嵌套循环的全表扫描——哪怕只连两张表、数据量刚过万行,响应也可能从几毫秒跳到几秒。这不是配置问题,是执行路径被硬性锁定。
- 检查方式:对查询跑
EXPLAIN,看key列是否为NULL,type是否是ALL或index - 单列索引够不够?够用但不最优——比如
ON u.id = o.user_id,只在orders.user_id建单列索引能避免全表扫描,但若还带WHERE o.status = 'paid',就可能失效 - 别依赖“主键自动有索引”:外键列(如
user_id)不会自动建索引,必须手动CREATE INDEX
复合索引字段顺序不是按ON出现顺序,而是按“等值→范围→排序”排
很多人建 (user_id, status) 是对的,但反过来建 (status, user_id) 就大概率失效——因为优化器只保证最左前缀匹配等值条件,一旦中间插了范围(IN、BETWEEN、>),右边字段就进不了索引查找路径。
- 典型错误:
ON a.x = b.x WHERE b.y IN ('a','b') ORDER BY b.z→ 复合索引应为(x, y, z),不是(x, z, y) - LEFT JOIN 场景更敏感:右表的关联字段 + WHERE 条件必须打包进同一个复合索引,否则右表可能被强制全扫
- MySQL 8.0+ 的 skip scan 不可靠:仅当前导列重复率极低时才触发,别把它当兜底方案
驱动表的筛选性直接决定索引能不能用上
即使所有关联字段都有完美索引,如果驱动表返回 10 万行,被驱动表就得做 10 万次索引查找(或哈希探测);而驱动表只返回 100 行,开销差三个数量级。优化器选错驱动表,索引就等于摆设。
- 查
EXPLAIN的rows列:驱动表(id 最小那行)的预估行数是否接近你实际要的数据量?远大于说明统计信息过期,该ANALYZE TABLE - 显式控制驱动顺序:MySQL 可用
/*+ JOIN_ORDER(t1, t2) */,TiDB 依赖准确统计信息自动重排,PG 则对WHERE下推更激进 - 先过滤再 JOIN 比先 JOIN 再 WHERE 更稳:把
WHERE order_time >= '2026-07-01'下推到子查询里,比放外层 WHERE 更容易命中索引
JOIN 字段类型不一致,索引直接失效
常见于字符串和数字混用:users.id 是 BIGINT,orders.user_id 是 VARCHAR,哪怕值看起来一样,MySQL 也会隐式转换,导致无法使用 orders.user_id 上的索引。
- 验证方法:看
EXPLAIN的Extra列是否含Using where; Using index—— 如果只有Using where,八成是类型不匹配或函数干扰 - 修复动作:统一字段类型,或显式
CAST(o.user_id AS SIGNED)(但不如改表结构彻底) - 时间字段也踩坑:
ON DATE(o.created_at) = u.date会丢索引,改成o.created_at >= u.date AND o.created_at
EXPLAIN 里的 rows、key 和 Extra 三列,缺一不可。










