慢查询识别:如何通过日志和监控发现慢查询?
慢查询是API性能的隐形杀手。很多时候,用户反馈“接口卡死了”,你查了一圈服务器CPU、内存、网络,都没问题。最后发现,是数据库里一条SQL跑了十几秒。
我个人习惯,排查API性能问题,第一步就是看慢查询日志。为什么?因为80%的API性能瓶颈,根因都在数据库查询上。你想想看,业务逻辑再复杂,也就是CPU算几下,但一条没走索引的全表扫描,能把整个数据库拖垮。
核心观点:没有慢查询日志的数据库,就像没有黑匣子的飞机。出了问题,你只能猜。
3.1 慢查询日志的配置
不同数据库的慢查询配置方式不一样。我主要讲MySQL和PostgreSQL,这两个是API后端最常用的。
3.1.1 MySQL 慢查询日志配置
MySQL的慢查询日志,说白了就是记录执行时间超过某个阈值的SQL。配置起来很简单,但很多人忽略了几个关键参数。
-- 查看当前慢查询配置
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'log_queries_not_using_indexes';
-- 动态开启(重启失效)
SET GLOBAL slow_query_log = ON;
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
SET GLOBAL long_query_time = 1; -- 单位:秒,建议设为1
SET GLOBAL log_queries_not_using_indexes = ON;
这里有个坑,我踩过好几次。long_query_time 默认是10秒。10秒?一个API接口如果等数据库10秒才返回,用户早跑了。我建议生产环境设成1秒,甚至0.5秒。为什么?因为很多慢查询其实在1-5秒之间,10秒的阈值会漏掉大量“慢性毒药”。
注意: log_queries_not_using_indexes 这个参数一定要开。它会把没走索引的查询也记录下来,哪怕执行时间没超过阈值。我在项目中遇到过,一条查询只跑了0.8秒,但没走索引,并发一上来,数据库CPU直接飙到100%。
3.1.2 PostgreSQL 慢查询配置
PostgreSQL的配置稍微复杂一点,但更灵活。它通过 auto_explain 模块来实现,可以记录执行计划和参数。
-- postgresql.conf 配置
shared_preload_libraries = 'auto_explain'
-- 重启后生效
-- 然后可以动态调整
SET auto_explain.log_min_duration = 1000; -- 单位:毫秒,1秒
SET auto_explain.log_analyze = ON;
SET auto_explain.log_buffers = ON;
SET auto_explain.log_nested_statements = ON;
我个人更喜欢PostgreSQL的慢查询日志,因为它能直接输出执行计划。你想想看,看到一条慢SQL的同时,还能看到它是怎么执行的——走没走索引、用了什么Join方式、扫描了多少行。这排查效率,比MySQL高出一大截。
3.2 慢查询日志的分析方法
日志有了,怎么看?别一条一条翻,那是原始人的做法。我们需要工具。
3.2.1 使用 pt-query-digest 分析
pt-query-digest 是Percona Toolkit里的神器。它能自动聚合、排序、统计慢查询日志,输出一份清晰的分析报告。
# 分析MySQL慢查询日志
pt-query-digest /var/log/mysql/mysql-slow.log
# 输出到文件
pt-query-digest /var/log/mysql/mysql-slow.log > slow_report.txt
# 分析最近1小时的日志
pt-query-digest --since='1 hour ago' /var/log/mysql/mysql-slow.log
输出的报告里,我最关注三个指标:
- Query Time (累计耗时):这条SQL总共花了多少时间。排名靠前的,就是头号嫌疑人。
- Execution Count (执行次数):执行次数多,哪怕单次不慢,累计时间也可能很可观。
- Rows Examine (扫描行数):扫描行数远大于返回行数,说明索引没用好。
经验之谈: 我一般先看 Query Time 排名前5 的SQL。如果这些SQL优化完,性能还没达标,再看执行次数多的。记住,二八原则——20%的慢查询消耗了80%的数据库时间。
3.2.2 手动分析关键字段
没有工具的时候,手动看日志也能发现线索。MySQL慢查询日志的格式是这样的:
# Time: 2024-01-15T10:30:00.123456Z
# User@Host: api_user[api_user] @ [10.0.0.1]
# Query_time: 5.234567 Lock_time: 0.001234 Rows_sent: 10 Rows_examined: 100000
SET timestamp=1705314600;
SELECT * FROM orders WHERE status = 'pending' ORDER BY created_at DESC;
看到 Rows_examined: 100000 和 Rows_sent: 10 了吗?扫描了10万行,只返回10行。这明显是索引问题。我遇到这种情况,第一反应就是看 status 和 created_at 有没有联合索引。
3.3 慢查询监控体系建设
光有日志还不够。日志是事后分析,我们要的是实时监控。出了问题,第一时间告警,而不是等用户投诉了才去翻日志。
3.3.1 监控指标与告警阈值
| 指标 | 告警阈值 | 说明 |
|---|---|---|
| 慢查询数量 (每分钟) | > 10 | 超过10条/分钟,说明系统有问题 |
| 最长查询时间 | > 5秒 | 单条查询超过5秒,必须立即处理 |
| 平均查询时间 | > 1秒 | 平均时间超过1秒,API响应肯定慢 |
| 全表扫描次数 | > 0 | 出现全表扫描,大概率缺索引 |
这些阈值不是死的。我一般会根据业务高峰期调整。比如双十一大促,慢查询数量阈值会放宽到50条/分钟,但最长查询时间还是5秒不变——用户可不管你是不是大促,等太久就是体验差。
3.3.2 监控工具选型
市面上工具很多,我挑几个常用的说说:
- Prometheus + Grafana:开源标配。用
mysqld_exporter采集MySQL指标,Grafana展示。可以自定义告警规则。 - Datadog:商业方案,开箱即用。慢查询、执行计划、索引使用情况,一个面板全搞定。就是贵。
- 阿里云RDS监控:如果数据库上云,直接用云厂商的监控。性能洞察、慢查询分析、索引优化建议,都有。
我的建议: 初创团队先用Prometheus+Grafana,免费且灵活。等业务规模大了,再考虑商业方案。别一上来就上Datadog,一个月几千美金的费用,小团队扛不住。
3.4 慢查询识别流程图
下面这张图,是我在实际项目中总结的慢查询识别流程。从日志采集到告警响应,一条线走通。
这个流程,说白了就是:采集 → 解析 → 计算 → 判断 → 告警。每一步都有对应的工具和配置。我建议你先把前两步(采集和解析)做好,再逐步完善告警。别想一口吃成胖子。
3.5 避坑指南
最后,分享几个我踩过的坑:
- 日志文件太大,磁盘写满:我曾经有一台数据库服务器,慢查询日志一天写了50GB,直接把磁盘撑爆了。解决方案:配置日志轮转,或者用
pt-query-digest实时分析后删除原始日志。 - 监控告警太频繁,变成“狼来了”:阈值设得太低,一天收到几百条告警,最后没人看了。我后来把告警分级:P0(最长查询时间>5秒)直接打电话,P1(慢查询数量>50条/分钟)发邮件,P2(平均查询时间>1秒)发群消息。
- 只监控不优化:监控的目的是发现问题,然后解决问题。我见过很多团队,监控面板做得花里胡哨,但慢查询一条都没优化。记住,监控是手段,不是目的。
最后提醒一句: 慢查询日志本身也会消耗性能。生产环境开启时,注意监控数据库的IO和CPU。如果发现日志写入影响了正常查询,可以考虑将日志写到独立的磁盘,或者使用异步日志。