加索引仍全表扫描主因是join多字段时单列索引无法自动组合,需按顺序创建匹配的复合索引,并确保字段类型一致、无隐式转换、无null干扰及函数表达式。

为什么加了索引还是全表扫描?
因为单列索引对 JOIN 条件不总是有效——尤其当关联条件含多个字段(比如 ON a.user_id = b.user_id AND a.status = b.status)时,MySQL/PostgreSQL 不会自动组合两个单列索引。优化器可能弃用索引,直接走全表扫描。
- 复合索引顺序必须匹配
JOIN中的字段顺序:先等值字段,再范围字段(如WHERE后的>或IN) - 如果
JOIN条件是a.x = b.x AND a.y = b.y,那a表上建(x, y),b表上也得建(x, y),不能只建(y, x) - 注意 NULL 值干扰:若关联字段允许 NULL,某些旧版本 MySQL 可能拒绝使用索引,建议显式加
IS NOT NULL过滤
EXPLAIN 看不出问题?重点盯这三个字段
EXPLAIN 输出里光看 type=ref 不够,真正关键的是 key、rows 和 Extra 三列:
-
key显示实际用的索引名 —— 如果是NULL,说明没走索引;如果是单列索引名但你写了多字段JOIN,大概率没生效 -
rows是预估扫描行数 —— 若接近表总行数(比如 100w 行表显示 98w),基本就是全表扫描了 -
Extra出现Using join buffer (Block Nested Loop)是危险信号,代表引擎放弃索引,改用缓存块嵌套循环,性能断崖下跌
LEFT JOIN 的索引陷阱比 INNER JOIN 更隐蔽
LEFT JOIN 容易让人误以为“驱动表有索引就行”,其实右表(被左连接的表)的索引质量直接影响性能,且优化器更倾向忽略右表索引:
- 驱动表(左表)必须有高效过滤条件(如带
WHERE的高选择性字段),否则即使右表有完美复合索引,也会因左表返回太多行而拖垮整体 - 右表的
ON字段必须全部落在同一复合索引最左前缀上;如果ON t1.id = t2.ref_id AND t2.deleted = 0,索引必须是(ref_id, deleted),反过来无效 - 避免在
LEFT JOIN右表的ON条件中用函数或表达式,比如ON t1.code = UPPER(t2.code),这会让索引完全失效
复合索引不是越多越好:小心索引膨胀和写放大
给每组 JOIN 条件都建复合索引,短期内见效快,但长期会显著拖慢 INSERT/UPDATE 性能,并占用更多磁盘和内存:
- 优先覆盖高频、高数据量的
JOIN场景,低频查询宁可用临时FORCE INDEX,也不盲目加索引 - 已有单列索引可复用时别重复建:比如已有
(user_id)和(status),新查user_id + status时,MySQL 5.6+ 支持索引合并(index_merge),但不如原生复合索引稳定 - 定期用
SHOW INDEX FROM table检查Cardinality,如果某索引基数远低于表行数(比如 100w 行,基数才 100),说明区分度极差,删掉它
最常被忽略的一点:JOIN 字段类型要严格一致。比如 INT 对 VARCHAR、UTF8MB4 对 UTF8,哪怕值看起来一样,也会导致隐式转换,索引直接失效。










