优化目标不是让 SQL “必须走索引”,而是在业务可接受的资源消耗下稳定返回。小表、低选择性条件或需要读取大量数据时,全表扫描可能反而更合理。

一、先拿到完整证据

生产排查至少需要:真实 SQL、绑定参数、表结构、索引、数据量、条件选择性、执行计划和实际耗时。缺任何一项,都可能把“经验”变成误判。

SHOW CREATE TABLE orders\G
SHOW INDEX FROM orders;

EXPLAIN FORMAT=TREE
SELECT id, user_id, status, created_at
FROM orders
WHERE user_id = 10086
  AND status = 'PAID'
ORDER BY created_at DESC
LIMIT 20;

能用 EXPLAIN ANALYZE 时再看实际执行

它会真正执行语句并给出实际行数和耗时。对 UPDATE、DELETE 或高成本查询,不要直接在生产库试跑。

二、六类高频原因

1. 根本没有可匹配的索引

WHERE、JOIN 与 ORDER BY 涉及的字段没有合适索引,或现有索引无法同时支持过滤与排序。先从查询访问路径设计,不要为每个字段各建一个单列索引。

2. 联合索引顺序不匹配

联合索引遵循从左向右的匹配逻辑。等值过滤通常放在前面,再结合范围与排序字段设计,但最终顺序仍要根据选择性、查询组合和写入成本验证。

索引查询条件判断
(user_id, status, created_at)user_id = ? AND status = ? ORDER BY created_at具备较好的连续匹配可能
(status, created_at)user_id = ? AND status = ?user_id 无法由该索引过滤
(user_id, created_at, status)user_id = ? AND created_at > ? AND status = ?范围条件后的列利用方式需看计划

3. 对索引列做函数或表达式计算

-- 不利于普通 created_at 索引
WHERE DATE(created_at) = '2026-08-07'

-- 改写为范围
WHERE created_at >= '2026-08-07 00:00:00'
  AND created_at <  '2026-08-08 00:00:00'

4. 隐式类型转换或字符集不一致

字符列拿数字比较、JOIN 两侧字符集或排序规则不同,都可能改变访问路径。核对字段类型、参数类型以及两张表连接列的定义。

5. 前导模糊匹配

LIKE '%keyword' 无法从普通 B-Tree 索引的有序前缀开始查找。可以评估前缀匹配、倒排索引或业务侧搜索方案,但不能只靠加普通索引解决。

6. 选择性低或成本估算认为全表扫描更便宜

状态字段只有少数几个值,查询又返回表中大部分行时,走索引会产生大量回表。此时优化器选择全表扫描可能是合理结果。也要检查统计信息是否能反映当前数据分布。

三、执行计划重点看什么

  • 实际选择的 key 与候选 possible_keys
  • 预估扫描 rows 与过滤比例 filtered
  • 访问类型与是否出现全索引/全表扫描
  • Extra 中的回表、临时表、排序与索引条件下推信息
  • EXPLAIN ANALYZE 的预估行数与实际行数差距

四、用一个案例完成优化闭环

订单列表按用户和状态查询最近 20 条。原表只有 user_id 单列索引,扫描同一用户的全部订单后再过滤状态和排序。

ALTER TABLE orders
  ADD INDEX idx_user_status_created
  (user_id, status, created_at DESC);

新索引不是“看到三个字段就拼起来”。它对应一个明确的高频查询模式:前两列等值过滤,第三列服务于排序和 LIMIT。上线前还要评估索引构建耗时、额外存储、写放大和重复索引。

五、优化后如何验证

  1. 计划验证:扫描行数、访问路径和排序方式符合预期。
  2. 结果验证:返回数据、排序和边界条件没有变化。
  3. 压测验证:比较 P95/P99、CPU、IO 与行扫描量,而不只看单次耗时。
  4. 灰度验证:观察业务峰值,保留撤回路径,并检查其他 SQL 是否受到影响。

FORCE INDEX 为什么不是首选

它把当前判断写死在 SQL 中。数据分布、版本或业务条件变化后,强制索引可能变成更差的路径。只有经过充分验证并设置复查机制时才谨慎使用。

六、一句话记忆

先确认“不走索引”是否真的慢,再用真实数据解释优化器为什么这样选;设计索引后,用执行计划、压测和业务指标完成验证。