MySQL慢查询诊断的系统化方法
MySQL慢查询是数据库性能问题的最常见表现,但诊断慢查询不能仅靠经验猜测。系统化的诊断流程包含三层:慢查询日志定位问题SQL、EXPLAIN分析执行计划、Performance Schema深入观测运行时指标。三层逐步深入,从”哪个SQL慢”到”为什么慢”再到”慢在哪里”,形成完整的诊断闭环。
慢查询日志配置与问题SQL筛选
慢查询日志是诊断的起点。MySQL 8.0中建议的配置参数:
-- my.cnf 核心配置
slow_query_log = ON
long_query_time = 0.5 -- 超过500ms的查询记录
log_queries_not_using_indexes = ON -- 未使用索引的查询也记录
slow_query_log_file = /var/log/mysql/slow.log
min_examined_row_limit = 100 -- 扫描行数低于100的查询忽略
-- 在线设置(无需重启)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;
SET GLOBAL log_queries_not_using_indexes = ON;
使用mysqldumpslow对慢日志进行聚合分析,快速定位高频慢查询:
# 按查询时间排序,取Top 10
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# 按出现次数排序,取Top 10
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log
EXPLAIN执行计划深度解读
EXPLAIN是分析SQL执行计划的核心工具。MySQL 8.0的EXPLAIN输出包含12列信息,重点关注以下5列:
type列:访问类型,从最优到最差排序为system > const > eq_ref > ref > range > index > ALL。出现ALL(全表扫描)时必须优化。
key列:实际使用的索引名。为NULL表示未使用索引。
rows列:预估扫描行数。高rows值通常是性能瓶颈的直接原因。
filtered列:过滤比例。低filtered值(<10%)意味着索引选择性差。
Extra列:额外信息。需重点关注的值包括Using filesort(额外排序)、Using temporary(临时表)、Using where(WHERE过滤)。
以下为典型慢查询的EXPLAIN分析与优化示例:
-- 原始查询:按创建时间范围筛选并按更新时间排序
SELECT id, title, status, updated_at
FROM orders
WHERE created_at BETWEEN '2026-07-01' AND '2026-08-01'
AND status IN ('pending', 'processing')
ORDER BY updated_at DESC
LIMIT 50;
-- EXPLAIN结果(优化前)
-- type: ALL, key: NULL, rows: 2850000, Extra: Using where; Using filesort
-- 优化方案:创建覆盖索引
ALTER TABLE orders
ADD INDEX idx_created_status_updated (created_at, status, updated_at);
-- EXPLAIN结果(优化后)
-- type: range, key: idx_created_status_updated,
-- rows: 18500, filtered: 100, Extra: Using index condition; Backward index scan
-- 扫描行数从285万降至1.85万,执行时间从2.3s降至0.02s
Performance Schema运行时观测
当EXPLAIN无法解释性能差异时(如数据分布不均匀导致的统计信息偏差),Performance Schema提供更底层的运行时指标。关键监控视图:
-- 查看当前正在执行的SQL及其等待事件
SELECT
t.PROCESSLIST_ID,
t.PROCESSLIST_INFO,
ew.EVENT_NAME AS wait_event,
ew.TIMER_WAIT / 1000000000 AS wait_ms
FROM performance_schema.threads t
JOIN performance_schema.events_waits_current ew
ON t.THREAD_ID = ew.THREAD_ID
WHERE t.PROCESSLIST_INFO IS NOT NULL
ORDER BY ew.TIMER_WAIT DESC
LIMIT 10;
-- 查看SQL语句的历史执行统计
SELECT
DIGEST_TEXT,
COUNT_STAR,
SUM_TIMER_WAIT / 1000000000 AS total_wait_s,
AVG_TIMER_WAIT / 1000000000 AS avg_wait_ms,
SUM_ROWS_EXAMINED,
SUM_ROWS_SENT
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 20;
索引优化策略与常见陷阱
最左前缀原则。联合索引(a, b, c)支持a、(a,b)、(a,b,c)三种查询条件组合,但不支持b或(b,c)作为查询条件的索引查找。在字段顺序设计时,将等值查询字段放在前面、范围查询字段放在后面:
-- 业务查询模式:WHERE user_id = ? AND created_at BETWEEN ? AND ?
-- 索引设计:等值字段在前,范围字段在后
ALTER TABLE orders
ADD INDEX idx_user_created (user_id, created_at);
索引选择性计算。选择性低的字段不适合单独建索引,可通过公式评估:
-- 计算各字段的选择性(越接近1越好)
SELECT
COUNT(DISTINCT status) / COUNT(*) AS status_selectivity,
COUNT(DISTINCT user_id) / COUNT(*) AS user_id_selectivity,
COUNT(DISTINCT created_at) / COUNT(*) AS created_at_selectivity
FROM orders;
-- 典型结果:status 0.001, user_id 0.85, created_at 0.99
-- status选择性极低,单独建索引无意义
索引失效的常见原因。对索引列使用函数(WHERE DATE(created_at) = ‘2026-08-06’)、隐式类型转换(VARCHAR列传入整型参数)、LIKE前缀通配符(LIKE ‘%keyword’)、OR条件中部分列无索引,均会导致索引失效。修复方式分别为:改用范围查询替代函数、确保参数类型一致、使用全文索引替代LIKE、拆分查询或覆盖OR两侧索引。
优化效果验证与持续监控
索引优化后,必须通过Performance Schema对比优化前后的执行统计,验证实际效果而非仅看EXPLAIN输出。持续监控方案:配置Prometheus MySQL Exporter采集慢查询指标,设置阈值告警,当慢查询频率突增时自动触发诊断流程。定期执行ANALYZE TABLE更新统计信息,避免统计信息过时导致执行计划退化。MySQL慢查询优化不是一次性工作,而是需要持续监控、诊断、优化的闭环工程。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql80-man-cha-xun-zhen-duan-yu-suo-yin-you-hua-shi-zhan/