mysql中分析慢查询日志进行索引优化

1次阅读

应重点查看慢查询日志中的Rows_examined和Rows_sent字段,若前者远大于后者(如124892 vs 10),说明存在索引缺失或失效;同时开启log_queries_not_using_indexes以捕获潜在风险语句。

mysql中分析慢查询日志进行索引优化

怎么看慢查询日志里真正耗时的 sql

mysql 的慢查询日志(slow_query_log)默认只记录执行时间超过 long_query_time 的语句,但光看耗时容易误判——比如一条 select 花了 2.3 秒,可能是因为没走索引全表扫描,也可能只是返回了 50 万行结果导致网络或客户端卡顿。

关键要看日志里的两个字段:Rows_examined(扫描行数)和 Rows_sent(返回行数)。如果前者远大于后者(比如 Rows_examined: 124892Rows_sent: 10),基本可以断定是索引缺失或失效。

  • 确保日志开启并记录足够信息:
    SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1; SET GLOBAL log_queries_not_using_indexes = ON;
  • log_queries_not_using_indexes 很重要——它会把没走索引的查询也记进来,哪怕执行很快,这类语句在数据增长后极易变慢
  • mysqldumpslow 快速聚合分析,例如:
    mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log

    (按总耗时排序,取前 10 条)

EXPLaiN 看懂执行计划的关键指标

EXPLAIN 不是“看看就行”,要盯住几个硬指标:

  • type 字段:优先级从高到低是 consteq_ref > ref > range > index > ALL。出现 ALL 就是全表扫描,必须优化
  • key 字段:显示实际使用的索引名。如果是 NULL,说明没走索引(注意:也可能是用了索引但被优化器放弃,需结合 possible_keysExtra 判断)
  • Extra 字段:警惕 Using filesort(需要额外排序)和 Using temporary(临时表),尤其是二者同时出现,往往意味着 ORDER BY + GROUP BY 没命中索引覆盖

示例:对 orders 表查最近 7 天未支付订单

EXPLAIN SELECT * FROM orders  WHERE status = 'pending' AND created_at > NOW() - INTERVAL 7 DAY;

typeALL,且 keyNULL,说明 statuscreated_at 缺少联合索引;建索引应按「等值条件在前、范围条件在后」原则:INDEX(status, created_at)

联合索引顺序怎么排才不踩坑?

联合索引不是字段随便砌,顺序直接影响能否命中。核心规则就一条:等值查询字段放前面,范围查询字段放后面

  • 错误示范:INDEX(created_at, status) —— WHERE status = 'pending' AND created_at > '2024-01-01' 只能用上 status 的等值部分,created_at 的范围无法利用索引排序,type 仍可能是 range 或更差
  • 正确写法:INDEX(status, created_at) —— 等值 status 定位数据块,再在块内按 created_at 范围扫描,type 可达 range,且支持后续 ORDER BY created_at
  • 如果还有 ORDER BY user_id,且 user_id 是等值条件,可加到索引末尾:INDEX(status, created_at, user_id);但若 user_id 是范围或 IN 列表,则不能继续延伸索引

哪些情况加了索引也没用?

不是所有字段都适合建索引,有些场景建了反而拖慢写入、浪费空间,甚至让优化器选错执行路径:

  • 低区分度字段:比如 gender(只有 ‘M’/’F’),is_deleted(99% 是 0),索引选择性太差,优化器大概率直接全表扫描
  • 频繁更新的字段:每条 UPDATE 都要维护索引树,写入压力大时得权衡读写比
  • LIKE 左模糊:WHERE name LIKE '%abc' 无法使用索引;右模糊 'abc%' 可以,但中间模糊 '%abc%' 不行(除非用全文索引或倒排)
  • 隐式类型转换WHERE phone = 13800138000phoneVARCHAR),MySQL 会把字段转成数字比较,导致索引失效;应统一为字符串WHERE phone = '13800138000'

最常被忽略的一点:索引列参与计算或函数,比如 WHERE YEAR(created_at) = 2024,哪怕 created_at 有索引也完全用不上。改写为范围查询:WHERE created_at >= '2024-01-01' AND created_at 才能走索引。

text=ZqhQzanResources