排查慢 SQL 是数据库运维和开发的核心技能。Oracle、MySQL 和 PostgreSQL 虽然内核不同,但排查逻辑相通:“发现 -> 定位 -> 分析 -> 优化”


🐬 一、MySQL:日志驱动型 (Log-Driven)

MySQL 的排查主要依赖慢查询日志 (Slow Query Log)。它像一个“保安”,只记录超过设定时间的“可疑人员”。

1. 如何开启与配置

MySQL 默认可能未开启慢查询日志,需手动配置。

临时生效(重启失效):

-- 1. 开启日志
SET GLOBAL slow_query_log = 'ON';
-- 2. 设置阈值(单位:秒),例如记录超过 1 秒的 SQL
SET GLOBAL long_query_time = 1; 
-- 3. 查看日志文件路径
SHOW VARIABLES LIKE 'slow_query_log_file';

永久生效:
修改 my.cnf (Linux) 或 my.ini (Windows):

[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = 1  -- 【重要】记录没走索引的 SQL,即使很快
2. 如何分析

直接看日志文件很痛苦,推荐使用官方工具 mysqldumpslow 或第三方神器 pt-query-digest (Percona Toolkit)。

使用 mysqldumpslow 排序查看 top 10 最耗时的 SQL:

mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# -s t: 按时间排序 (time)
# -t 10: 取前10条

核心分析工具:EXPLAIN
找到慢 SQL 后,必须在前面加 EXPLAIN 运行,查看执行计划:

EXPLAIN SELECT * FROM orders WHERE user_id = 100 AND status = 'PAID';

重点关注字段:

  • type: 访问类型。ALL (全表扫描,最差) > index > range > ref > eq_ref > const (最好)。
  • key: 实际使用的索引。如果是 NULL,说明没走索引。
  • rows: 预估扫描行数。越大越慢。
  • Extra: 如果出现 Using filesort (文件排序) 或 Using temporary (临时表),通常需要优化。
💡 生动比喻

MySQL 的慢查询日志就像小区的**“迟到登记本”**。

  • long_query_time 是门卫设定的迟到阈值(比如 8:05 算迟到)。
  • 一旦有人超过这个时间进门,门卫就记下来。
  • EXPLAIN 就像是调取监控录像,分析这个人为什么迟到:是路堵了(全表扫描)?还是他绕远路了(没走索引)?

🐘 二、PostgreSQL:模块监控型 (Module-Monitoring)

PostgreSQL 不推荐频繁使用日志文件(因为日志量巨大且难分析),而是推崇使用内置的强大扩展模块 pg_stat_statements。它像飞机的“黑匣子”,记录所有飞行的详细数据。

1. 开启 pg_stat_statements (黄金标准)

这是 PG 排查慢 SQL 的绝对核心

步骤 A:配置文件 (postgresql.conf)

shared_preload_libraries = 'pg_stat_statements'  # 必须重启生效
pg_stat_statements.track = all                   # 跟踪所有语句
pg_stat_statements.max = 10000                   # 记录的最大语句数

修改后需重启数据库服务。

步骤 B:创建扩展

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
2. 如何分析

直接查询系统视图,找出“总耗时最长”或“调用次数最多”的 SQL。

查询 Top 10 最耗时的 SQL:

SELECT 
    query, 
    calls, 
    total_exec_time, 
    mean_exec_time, 
    rows 
FROM pg_stat_statements 
ORDER BY total_exec_time DESC 
LIMIT 10;
  • total_exec_time: 总耗时(可能是调用次数多导致的)。
  • mean_exec_time: 平均每次耗时(真正的慢 SQL)。

核心分析工具:EXPLAIN (ANALYZE, BUFFERS)
PG 的 EXPLAIN 比 MySQL 更强大,建议加上 ANALYZE (真正执行一次) 和 BUFFERS (查看 IO 命中)。

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM users WHERE email LIKE '%@gmail.com';

重点关注:

  • Execution Time: 真实执行时间。
  • Plan Nodes:
    • Seq Scan: 全表扫描(坏)。
    • Index Scan / Index Only Scan: 索引扫描(好)。
    • Hash Join / Nested Loop: 连接算法是否合理。
  • Buffers: hit (内存命中) 多还是 read (磁盘读取) 多?磁盘读多是性能杀手。
💡 生动比喻

PostgreSQL 的 pg_stat_statements 就像医院的**“电子病历系统”**。

  • 它不只记录“病危”病人(慢 SQL),而是记录所有病人的就诊数据。
  • 医生(DBA)可以随时拉报表:哪种病(SQL 类型)发作最频繁?哪种药(执行计划)效果最差?
  • EXPLAIN (ANALYZE) 就像是**“模拟手术”**,在真正动刀前,先演练一遍,看看哪里会出血(IO 瓶颈),哪里会堵塞(锁等待)。

🏛️ 三、Oracle:视图诊断型 (View-Diagnostic)

Oracle 是企业级数据库,它的核心理念是**“一切皆视图”**。不需要改配置文件,只要数据库在跑,数据就在内存视图里等着你去查。

1. 实时排查 (当前正在跑的慢 SQL)

如果系统现在正卡,查 v$sessionv$sql

-- 查找当前正在执行且耗时较长的会话
SELECT s.sid, s.serial#, s.username, q.sql_text, s.last_call_et 
FROM v$session s, v$sql q 
WHERE s.sql_id = q.sql_id 
  AND s.status = 'ACTIVE' 
  AND s.username IS NOT NULL 
ORDER BY s.last_call_et DESC;
2. 历史排查 (过去跑过的慢 SQL)

Oracle 有强大的 AWR (Automatic Workload Repository) 报告和历史视图 v$sqlareadba_hist_sqlstat

方法 A:直接查视图 (简单快捷)

-- 按“总_elapsed_time”找出 Top 10 SQL
SELECT sql_id, executions, elapsed_time/1000000 as total_sec, sql_text
FROM (
    SELECT sql_id, executions, elapsed_time, sql_text
    FROM v$sqlarea
    ORDER BY elapsed_time DESC
)
WHERE ROWNUM <= 10;

方法 B:生成 AWR 报告 (专业深度)
这是 Oracle DBA 的终极武器。

-- 1. 查看可用的快照 ID
SELECT snap_id, begin_interval_time, end_interval_time FROM dba_hist_snapshot ORDER BY snap_id DESC FETCH FIRST 5 ROWS ONLY;

-- 2. 生成报告 (在 SQL*Plus 中运行)
@$ORACLE_HOME/rdbms/admin/awrrpt.sql
-- 输入开始快照 ID 和 结束快照 ID,它会生成一个详细的 HTML/TXT 报告,包含:
-- * Load Profile (负载概况)
-- * SQL ordered by Elapsed Time (按耗时排序的 SQL)
-- * Wait Events (等待事件,判断是 CPU 不够还是 IO 太慢)

核心分析工具:EXPLAIN PLAN & DBMS_XPLAN

-- 1. 解释计划
EXPLAIN PLAN FOR SELECT * FROM emp WHERE deptno = 10;

-- 2. 查看格式化输出
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

重点关注:

  • Cost: 优化器预估的成本,越低越好。
  • Operation: TABLE ACCESS FULL (全表扫描) 通常是优化目标。
  • Wait Events (在 AWR 报告中): db file scattered read (多块读,通常全表扫描), db file sequential read (单块读,通常索引), enq: TX - row lock contention (行锁争用)。
💡 生动比喻

Oracle 就像一架波音 747 的驾驶舱

  • 仪表盘上(v$ 视图)密密麻麻全是实时数据:油压、速度、引擎温度。
  • 你不需要去翻黑匣子(日志),直接看仪表盘就知道哪个引擎(SQL)过热了。
  • AWR 报告 就像是**“飞行数据记录仪分析报告”**,航班结束后,工程师可以复盘整个飞行过程,精确到每一秒的油耗和风速,找出为什么这次飞行比上次慢了 10 分钟。

⚔️ 四、横向对比总结

特性 MySQL PostgreSQL Oracle
核心机制 日志文件 (Slow Log) 内存模块 (pg_stat_statements) 动态视图 (v$sql, AWR)
开启难度 低 (配置参数即可) 中 (需修改 config 并重启,建扩展) 无 (默认开启,企业版 AWR 需 License)
性能影响 高并发下写日志可能有轻微 IO 开销 极低 (内存统计) 极低 (内存统计),AWR 采集有微小开销
分析工具 mysqldumpslow, pt-query-digest 直接 SQL 查询 pg_stat_statements awrrpt.sql, ASH Report
执行计划 EXPLAIN (静态为主) EXPLAIN (ANALYZE, BUFFERS) (动态+IO) DBMS_XPLAN + Wait Events (等待事件)
最大痛点 日志轮转管理,格式解析麻烦 忘记开启 pg_stat_statements AWR 功能需要付费 License (19c 后部分受限)
一句话口诀 查日志,看 Explain 开插件,查视图,跑 Analyze 查 V$视图,跑 AWR 报告

🚀 五、通用优化思路 (不管用什么库)

  1. 索引是王道:80% 的慢 SQL 是因为没走索引或索引失效(如对字段做计算、LIKE '%...')。
  2. 拒绝 SELECT *:只查需要的列,利用覆盖索引减少回表。
  3. 小表驱动大表:在 JOIN 操作中,确保数据量小的表驱动数据量大的表。
  4. 分页优化:深分页 (LIMIT 100000, 10) 是性能杀手,改用 WHERE id > last_id 方式。
  5. 硬件与配置
    • MySQL: 调整 innodb_buffer_pool_size
    • PG: 调整 shared_buffers, work_mem, effective_cache_size
    • Oracle: 调整 SGA, PGA 大小。

建议

  • MySQL 用户:务必开启 log_queries_not_using_indexes,它能帮你抓到那些“跑得快但没走索引”的隐患 SQL。
  • PostgreSQL 用户:生产环境必须开启 pg_stat_statements,这是 PG 的灵魂。
  • Oracle 用户:学会看 AWR 报告中的 “Top 5 Timed Foreground Events”,它能直接告诉你系统是卡在 CPU、IO 还是锁上。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐