1. 慢查询SQL的定位价值与核心原理
数据库性能优化中,慢查询SQL就像隐藏在系统里的"血栓",它们悄无声息地消耗着系统资源,最终可能导致整个应用响应迟缓。MySQL作为最流行的关系型数据库,其慢查询日志功能就是我们排查性能问题的"X光机"。
慢查询的界定标准通常由两个参数控制:
long_query_time:默认10秒,表示执行时间超过该值的SQL会被记录min_examined_row_limit:默认0,表示需要检查的最小行数阈值
实际生产环境中,建议将long_query_time设置为1-3秒(电商大促期间甚至需要调整到500毫秒)。我曾经处理过一个案例,某金融系统将阈值设为5秒,结果漏掉了大量2-3秒的查询,这些查询在并发场景下直接拖垮了整个集群。
2. 慢查询日志的配置与启用
2.1 基础配置方法
在my.cnf配置文件中添加以下参数(动态修改需执行SET GLOBAL):
ini复制slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1
log_queries_not_using_indexes = 1
重要提示:开启
log_queries_not_using_indexes会显著增加日志量,建议在问题排查期间临时开启
2.2 文件轮转策略
慢查询日志会持续增长,需要配置日志轮转。使用logrotate的配置示例:
conf复制/var/log/mysql/mysql-slow.log {
daily
rotate 7
missingok
compress
delaycompress
notifempty
create 640 mysql mysql
postrotate
mysqladmin flush-logs
endscript
}
3. 慢查询分析工具详解
3.1 mysqldumpslow工具
MySQL自带的解析工具,基本用法:
bash复制# 查看执行时间最长的10个查询
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log
# 查看锁定时间最长的查询
mysqldumpslow -s l -t 10 /var/log/mysql/mysql-slow.log
输出示例:
code复制Count: 5 Time=12.34s (61s) Lock=0.00s (0s) Rows=1000.0 (5000)
SELECT * FROM orders WHERE user_id=N AND status='pending'
3.2 pt-query-digest进阶分析
Percona Toolkit提供的专业工具,安装方法:
bash复制wget percona.com/get/pt-query-digest
chmod +x pt-query-digest
典型分析命令:
bash复制pt-query-digest --limit=10% --filter '$event->{fingerprint} =~ m/^select/i' \
/var/log/mysql/mysql-slow.log > slow_report.txt
输出报告包含:
- 查询响应时间占比
- 执行频率统计
- 表扫描情况分析
- 索引使用建议
4. 性能模式(Performance Schema)监控
MySQL 5.7+版本推荐使用performance_schema进行实时监控:
sql复制-- 启用events_statements_history_long
UPDATE performance_schema.setup_consumers
SET ENABLED = 'YES'
WHERE NAME = 'events_statements_history_long';
-- 查询慢SQL(示例)
SELECT * FROM performance_schema.events_statements_history_long
WHERE SQL_TEXT IS NOT NULL
AND TIMER_WAIT > 1000000000 -- 1秒=1000000000皮秒
ORDER BY TIMER_WAIT DESC
LIMIT 10;
关键字段说明:
TIMER_WAIT:执行总时间(皮秒)LOCK_TIME:锁定时间ROWS_EXAMINED:检查行数ROWS_SENT:返回行数
5. 系统表与INFORMATION_SCHEMA查询
通过sys库快速定位问题(MySQL 5.7+):
sql复制-- 查看全表扫描最多的语句
SELECT * FROM sys.statements_with_full_table_scans
LIMIT 10;
-- 查看排序成本高的查询
SELECT * FROM sys.statements_with_sorting
WHERE exec_count > 100
ORDER BY sort_merge_passes DESC
LIMIT 10;
6. 慢查询的典型模式与优化方向
6.1 全表扫描特征
ROWS_EXAMINED远大于ROWS_SENT- 执行计划显示
type=ALL - 解决方案:添加合适的索引或优化WHERE条件
6.2 临时表问题
- 出现
Using temporary提示 - 常见于GROUP BY、DISTINCT操作
- 解决方案:优化SQL写法或调整tmp_table_size
6.3 文件排序特征
- 出现
Using filesort提示 - 排序字段无索引支持
- 解决方案:为ORDER BY字段添加索引
7. 生产环境排查实战案例
某电商平台大促期间出现数据库负载飙升,通过以下步骤定位问题:
- 临时将
long_query_time调整为0.5秒 - 使用pt-query-digest分析高峰时段日志
- 发现某个商品查询SQL执行频率异常:
sql复制SELECT * FROM products WHERE category_id=? AND status='on_sale' ORDER BY price DESC LIMIT 100 - 检查发现category_id和status有索引,但缺少复合索引
- 添加索引:
ALTER TABLE products ADD INDEX idx_cat_status_price (category_id, status, price) - 查询时间从1.8秒降至0.02秒
8. 监控体系搭建建议
完善的慢查询监控应包含:
- 实时告警:对执行时间超过阈值的SQL触发告警
- 趋势分析:统计慢查询的数量变化趋势
- 自动归类:将相似SQL归类统计
- 影响评估:计算慢查询消耗的总资源占比
推荐工具组合:
- Prometheus + Grafana:用于可视化监控
- pt-query-digest:用于定期分析报告
- PMM(Percona Monitoring and Management):一体化监控方案
9. 常见误区与避坑指南
- 不要盲目依赖EXPLAIN:执行计划会因数据量变化而改变,应该用真实数据测试
- 避免过度索引:每个额外索引都会增加写操作成本
- 注意隐式类型转换:如
WHERE varchar_col=123会导致索引失效 - 分页查询优化:
LIMIT 10000,10会导致扫描大量行,建议改用游标方式 - 批量操作替代循环:用
INSERT ... VALUES(...),(...)代替多次单条INSERT
10. 高级技巧:SQL重写优化实例
原始慢查询:
sql复制SELECT DISTINCT u.* FROM users u
JOIN orders o ON u.id=o.user_id
WHERE o.create_time > '2023-01-01'
AND u.status = 'active'
ORDER BY u.reg_time DESC
LIMIT 100;
优化方案:
- 去除不必要的DISTINCT
- 改用EXISTS替代JOIN
- 确保排序字段有索引
优化后SQL:
sql复制SELECT u.* FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id=u.id
AND o.create_time > '2023-01-01'
)
AND u.status = 'active'
ORDER BY u.reg_time DESC
LIMIT 100;
配合索引:
sql复制ALTER TABLE users ADD INDEX idx_status_regtime (status, reg_time);
ALTER TABLE orders ADD INDEX idx_userid_createtime (user_id, create_time);