mysql索引优化常见错误有哪些_mysql避坑指南

5次阅读

索引列顺序必须按等值过滤列优先、范围查询列靠后、排序分组列最后排列,否则最左前缀匹配失效;select * 无法触发覆盖索引;对索引列使用函数或表达式会导致索引失效。

mysql索引优化常见错误有哪些_mysql避坑指南

索引列顺序写反了,WHERE a = ? AND b = ? 却建了 (b, a)

复合索引的列顺序不是随便排的。mysql 用最左前缀匹配,(b, a) 索引对 WHERE a = ? 完全无效,连 WHERE b = ? AND a = ? 也只用上第一层。实际执行时可能走全表扫描。

实操建议:

  • 按查询条件中等值过滤列优先、范围查询列靠后、排序/分组列最后的顺序建索引
  • WHERE tenant_id = ? AND status = ? ORDER BY created_at DESC → 推荐索引:(tenant_id, status, created_at)
  • EXPLAINkey_lenExtra 字段:如果 key_len 明显偏小,或出现 using filesort,大概率顺序不合理

SELECT * 搭配覆盖索引失效

覆盖索引要求查询字段全部被索引包含,但 SELECT * 会拉出所有列,哪怕你建了 (a, b, c) 索引,只要表里还有 d 字段,就无法触发覆盖,必然回表。

实操建议:

  • 明确只查需要的字段,比如改 SELECT *SELECT a, b, c
  • 若高频查询固定几列,可建联合索引并确保字段全覆盖,例如:CREATE INDEX idx_cover ON t (a, b, c);
  • 注意 TEXT/BLOB 类型不能建在索引中,即使声明了也会被忽略,导致你以为“覆盖”了其实没覆盖

在索引列上做函数或计算,WHERE YEAR(created_at) = 2024

只要对索引列用了函数、表达式、类型转换,索引就直接失效。MySQL 无法用 B+ 树快速定位 YEAR(created_at) 的结果,只能全扫。

实操建议:

  • 把函数移到右边:WHERE created_at >= '2024-01-01' AND created_at
  • 避免隐式类型转换WHERE user_id = '123'user_idint)会触发转换,改成 WHERE user_id = 123
  • 字符串比较注意 collation,utf8mb4_0900_as_csutf8mb4_general_ci 混用可能导致索引跳过

过度索引:给每个 WHERE 字段都单独建索引

单列索引多而杂,不仅不加速,反而拖慢写入(每次 INSERT/UPDATE 都要维护多个 B+ 树),还可能让优化器选错执行计划。

实操建议:

  • 优先合并:已有 (a, b) 索引时,WHERE a = ?WHERE a = ? AND b = ? 都能用上,不必再建 (a)
  • 删掉长期没用的索引:查 sys.schema_unused_indexes 视图(MySQL 8.0+),或用 performance_schema.table_io_waits_summary_by_index_usage
  • 注意前缀索引陷阱:INDEX (email(16)) 可能导致 WHERE email = 'x@y.z' 匹配失败,除非所有邮箱前 16 字符都唯一

索引优化真正的难点不在“建不建”,而在“查什么、怎么查、数据长什么样”。线上慢查必须结合 EXPLAIN、真实数据分布、QPS 和写入压力一起看,脱离场景谈索引,90% 是纸上谈兵。

text=ZqhQzanResources