ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MySQL索引失效的9种场景与优化实战

MySQL索引失效的9种场景与优化实战

1. 索引失效的典型场景分析

慢查询是数据库性能优化的头号敌人,而索引失效则是导致慢查询的罪魁祸首。根据多年DBA经验,90%的生产环境性能问题都源于索引使用不当。下面我将结合实战案例,剖析最常见的9种索引失效场景。

1.1 隐式类型转换陷阱

当查询条件的数据类型与索引列定义不一致时,MySQL会进行隐式类型转换,导致索引失效。例如:

-- user_id字段为varchar类型但建立了索引 SELECT * FROM users WHERE user_id = 10086; -- 失效 SELECT * FROM users WHERE user_id = '10086'; -- 有效

注意:所有与索引列比较的值,必须严格匹配字段定义的数据类型。日期字段尤其需要注意格式一致性。

1.2 最左前缀原则违反

联合索引遵循最左匹配原则,以下情况会导致索引失效:

-- 联合索引为 (status, create_time) SELECT * FROM orders WHERE create_time > '2023-01-01'; -- 失效 SELECT * FROM orders WHERE status = 1 AND create_time > '2023-01-01'; -- 有效

实测案例:某电商平台订单查询性能从2秒提升到50ms,仅通过调整查询条件顺序。

1.3 索引列参与运算

在索引列上使用函数或运算会导致索引失效:

-- create_time字段有索引 SELECT * FROM logs WHERE YEAR(create_time) = 2023; -- 失效 SELECT * FROM logs WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'; -- 有效

替代方案:使用范围查询或冗余存储计算结果的派生列。

2. 优化器行为导致的索引失效

2.1 统计信息不准确

MySQL优化器依赖统计信息选择执行计划。当统计信息过时时:

ANALYZE TABLE orders; -- 更新统计信息

经验:大表数据变化超过10%时应手动更新统计信息,特别是After凌晨批量作业。

2.2 索引选择性不足

当索引列不同值较少时(如性别、状态码),优化器可能选择全表扫描:

-- 订单状态字段只有0-5六个值 SELECT * FROM orders WHERE status = 1; -- 可能失效

解决方案:考虑使用复合索引或强制索引:

SELECT * FROM orders FORCE INDEX(idx_status) WHERE status = 1;

2.3 OR条件处理

使用OR连接不同字段的条件时:

-- name和phone字段分别有索引 SELECT * FROM users WHERE name = '张三' OR phone = '13800138000'; -- 失效

优化方案:改用UNION ALL:

SELECT * FROM users WHERE name = '张三' UNION ALL SELECT * FROM users WHERE phone = '13800138000';

3. 索引设计缺陷

3.1 过长的索引列

索引列长度过长会导致:

  • 索引存储空间暴增
  • 查询效率下降
-- 优化前 ALTER TABLE articles ADD INDEX idx_content(content); -- content是TEXT类型 -- 优化后 ALTER TABLE articles ADD INDEX idx_content_prefix(content(100)); -- 只索引前100字符

3.2 不当的索引顺序

联合索引列顺序严重影响查询效率:

-- 场景:90%查询按时间范围筛选,10%按状态 -- 错误设计 ALTER TABLE orders ADD INDEX idx_status_time(status, create_time); -- 正确设计 ALTER TABLE orders ADD INDEX idx_time_status(create_time, status);

3.3 冗余索引

重复索引会降低写入性能:

-- 冗余索引示例 ALTER TABLE users ADD INDEX idx_email(email); ALTER TABLE users ADD INDEX idx_email_phone(email, phone); -- 包含前一个索引

检查工具:

SELECT * FROM sys.schema_redundant_indexes;

4. 高级排查技巧

4.1 执行计划分析

使用EXPLAIN查看索引使用情况:

EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 1 AND amount > 100;

重点关注:

  • key:实际使用的索引
  • type:访问类型(range优于index)
  • rows:预估扫描行数

4.2 性能监控

开启慢查询日志:

-- my.cnf配置 slow_query_log = 1 slow_query_log_file = /var/log/mysql/mysql-slow.log long_query_time = 1 log_queries_not_using_indexes = 1

分析工具:

mysqldumpslow -s t /var/log/mysql/mysql-slow.log

4.3 索引优化实战案例

某金融系统交易表优化前后对比:

指标优化前优化后
查询耗时1200ms35ms
CPU占用80%15%
索引大小12GB4.8GB

优化措施:

  1. 重建联合索引调整列顺序
  2. 增加覆盖索引
  3. 移除3个冗余索引

5. 避坑指南

5.1 索引创建原则

  • 优先为WHERE条件列创建索引
  • 联合索引按区分度降序排列
  • 避免在更新频繁的列上建索引
  • 单表索引数建议不超过5个

5.2 日常维护建议

  • 每周检查information_schema.statistics
  • 监控Handler_read%状态变量
  • 定期使用pt-index-usage分析索引使用率
  • 大表ALTER操作使用Online DDL工具

5.3 紧急处理方案

当出现索引突然失效时:

  1. 检查表统计信息
  2. 验证SQL执行计划
  3. 考虑使用FORCE INDEX临时解决
  4. 在低峰期重建索引
-- 索引重建命令 ALTER TABLE orders ENGINE=InnoDB; ANALYZE TABLE orders;
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进