
十五年数据库相关经验,做过 DBA、架构师、技术顾问。不求"颠覆",只求"靠谱"。
翻过很多文档,也踩过很多坑。做 DBA 这些年,凌晨被叫醒的次数里,有三分之一是因为"系统变慢了"。
"慢"这个描述最折磨人。它不像"宕机"那样明确——系统还在跑,只是比平时慢了。慢了多久?谁慢了?为什么慢?一问三不知。
这篇文章我把慢查询排查的方法论整理出来,从"听到业务说慢"到"定位到具体 SQL 和根因",每一步都说清楚。
接到"系统变慢了"的反馈,第一步不是去查数据库,是先确认到底是不是数据库的问题。
怎么确认?
看三个指标:
我的习惯:接到慢的反馈,我会先问三个问题——"什么时候开始慢的?""是所有操作都慢还是某个操作慢?""大概慢了多少?"这三个问题能帮我快速缩小排查范围。
确认了是数据库的问题,接下来要找"谁在慢"。
这是最直接的途径。所有主流数据库都有慢查询日志功能。
MySQL:在配置文件里设置 slow_query_log = ON,long_query_time = 1(超过 1 秒的查询记入日志)。日志文件里会记录每条慢 SQL 的执行时间、锁等待时间、扫描行数。
PostgreSQL:设置 log_min_duration_statement = 1000,超过 1 秒的语句会被记录。
Oracle:通过 AWR 报告或者启用 SQL Trace 来捕获慢查询。
慢查询日志是排查的第一手证据。但要注意,它只记录了"已经慢了的 SQL",不记录"为什么慢"。根因还需要进一步分析。
慢查询日志有延迟(日志是事后记录的)。如果系统正在慢,需要看实时状态。
MySQL:查询 information_schema.processlist 表,能看到当前所有正在执行的 SQL、执行时长、当前状态。重点关注 Time 字段很大的那些——它们就是正在拖慢系统的 SQL。
PostgreSQL:查询 pg_stat_activity 视图,同样能看到所有活跃会话和正在执行的查询。
紧急情况下:如果某条 SQL 正在长时间执行并且阻塞了其他会话,可以直接 KILL 掉它,先恢复业务,再分析根因。
对于复杂场景,光看日志和会话不够,需要更系统化的分析。
MySQL 有几个常用的性能视图:
performance_schema.events_statements_summary_by_digest:按 SQL 模板汇总执行次数、总耗时、平均耗时。能帮你找到"不是单次慢,但执行频率极高"的 SQL。sys.schema_table_statistics:按表汇总 I/O 统计。能帮你找到"哪张表是 I/O 热点"。PostgreSQL 有 pg_stat_statements 扩展,功能和 MySQL 的 digest 汇总类似。
经验:有时候最慢的不是某一条 SQL,而是某条 SQL 被高频执行了上万次。单次 10ms,执行一万次就是 100 秒。这种问题在慢查询日志里看不出来(因为单次没超过阈值),需要用汇总视图才能发现。
找到慢 SQL 后,接下来分析"为什么慢"。我总结了一个检查清单,按顺序排查,90% 的慢查询都能定位到原因。
这是慢查询的头号原因。SQL 没有走索引,导致全表扫描。
怎么判断? 用 EXPLAIN 看执行计划。如果 type 列是 ALL(MySQL)或者 Seq Scan(PostgreSQL),说明是全表扫描。
为什么没走索引? 常见情况:
WHERE YEAR(create_time) = 2024,create_time 上有索引但被函数包裹后失效)。LIKE '%张三',索引无法利用)。怎么解决? 加索引,或者改写 SQL 让索引生效。
有时候 SQL 走了索引,但还是很慢。这是因为优化器选了"错误的索引"。
举个例子:表上有两个索引——idx_name 和 idx_create_time。SQL 是 SELECT * FROM users WHERE name = '张三' ORDER BY create_time DESC。优化器可能选择用 idx_name 做过滤,然后用 filesort 排序。但如果数据量很大,filesort 会很慢。
怎么判断? 看 EXPLAIN 输出里的 key 列,确认用了哪个索引。同时看 Extra 列,如果有 Using filesort 或 Using temporary,说明有额外的排序或临时表开销。
怎么解决?
idx_name_create_time (name, create_time)。FORCE INDEX 手动指定索引(不推荐作为长期方案,但可以用于紧急救火)。这是容易被忽视的原因。索引没错,SQL 也没错,但就是变慢了。
根因:数据库的优化器依赖统计信息来做执行计划决策。统计信息是定期更新的(MySQL 默认是自动的,但更新频率有限)。如果数据量发生了巨大变化(比如某张表从 100 万行涨到 1000 万行),统计信息还没来得及更新,优化器还基于旧的统计信息做决策,执行计划就会选错。
怎么判断? 对比"慢的时候"和"快的时候"的执行计划,如果不一样,大概率是统计信息过时导致的。
怎么解决? 手动更新统计信息——MySQL 用 ANALYZE TABLE,PostgreSQL 用 ANALYZE,Oracle 用 DBMS_STATS.GATHER_TABLE_STATS。
这个问题在数据量快速增长的系统中特别常见。比如电商大促后、月底结算后,数据量突然翻倍,慢查询就来了。
SQL 本身不慢,但在等锁。
怎么判断? 看慢查询日志里的 Lock_time 字段(MySQL)。如果 Lock_time 占了大部分执行时间,说明是在等锁,不是查询本身慢。
为什么锁等待? 常见情况:
怎么解决? 参考我上一篇文章的死锁排查方法——找到阻塞源,优化事务长度和加锁顺序。
SQL 和索引都没问题,但还是慢。那可能是底层资源不够了。
排查方向:
iostat 看磁盘利用率。如果 iowait 很高,说明磁盘是瓶颈。怎么解决? 扩容硬件,或者优化查询减少资源消耗。有时候加一块 SSD 比调一百条 SQL 都管用。
排查慢查询是事后救火,定期巡检才是正道。
我的日常巡检清单(建议每周执行一次):
sys.schema_unused_indexes 找出没被使用的索引(占空间还影响写入性能)。OPTIMIZE TABLE 定期整理。慢查询排查就一套流程:确认慢 → 找 SQL → 看执行计划 → 查根因。
执行计划是排查的核心工具。看懂了执行计划里的扫描方式、索引使用、排序方法,根因自然就出来了。
预防慢查询也是日常功夫:定期巡检、及时更新统计信息、合理设计索引。
处理过上百次慢查询问题,每次回到这套流程,问题都能解决。不需要什么高级工具,耐心和方法论就够了。
下一篇我会讲连接池配置和连接泄漏排查,把数据库运维的几个高频问题收个尾。
有问题评论区见。
十五年数据库领域老炮。关注我,一起把数据库这件事搞明白。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。