慢查询应急需“稳准狠”:5分钟内定位、3分钟干预、1分钟验证;优先kill会话止血,禁盲目加索引;用sql改写、hint、限流兜底;所有操作须留痕、可回退、可验证。

遇到SQL生产慢查询,第一反应不是查原因,而是快速止血——阻断对数据库的持续冲击,保障核心业务可用。关键在“快”:5分钟内识别、3分钟内干预、1分钟内验证效果。
一、实时定位罪魁SQL(不依赖历史日志)
直接登录数据库服务器,用系统视图或命令秒级抓取正在运行的长耗时SQL:
- mysql:执行 SHOW PROCESSLIST,重点关注 Time > 60 且 State = “Sending data” / “Copying to tmp table” 的连接;配合 SELECT * FROM information_schema.PROCESSLIST WHERE TIME > 60;
- postgresql:查询 pg_stat_activity,筛选 state = ‘active’ 且 backend_start ,再按 backend_start 倒序;加 pg_blocking_pids(pid) 快速判断是否被阻塞
- oracle:查 v$session 和 v$sql 关联,用 sql_id 和 last_call_et > 60 定位活跃慢SQL
二、立即终止(KILL)而非等待超时
确认SQL无业务强依赖(如非事务关键路径、非金融最终一致性写入),果断终止会话:
- MySQL:执行 KILL [id](id 来自 PROCESSLIST 中的 Id 字段);若担心误杀,先 KILL QUERY [id] 只终止当前语句,保留连接
- PostgreSQL:执行 select pg_terminate_backend(pid);若只想取消当前查询,用 pg_cancel_backend(pid)
- 注意:KILL 操作本身几乎无延迟,但需确保有对应权限(如 MySQL 的 PROCESS + SUPER 权限)
三、临时降级:加索引?先别动!用覆盖手段兜底
线上加索引可能锁表、阻塞DML,风险高。应急阶段优先用更安全的“逻辑兜底”:
- 改写SQL:去掉 SELECT *,明确字段;拆分大 LIMIT OFFSET(如 LIMIT 10000,20 → 改为基于主键/时间戳的游标分页)
- 加查询提示(Hint):MySQL 用 /*+ MAX_EXECUTION_TIME(3000) */ 强制超时;PG 用 SET LOCAL statement_timeout = ‘3s’(需在事务内设置)
- 前置限流:在应用层(如 nginx、API 网关)对该接口做 QPS 限流(如限制为 1/s),避免重试风暴
四、回滚必须可验证、可逆、带记录
任何变更操作(包括 KILL、参数调整、SQL改写上线)都必须留痕并验证回退路径:
- 执行前,用 mysqlbinlog / pg_waldump 记录当前 binlog/wal 位置,或截图 SHOW MASTER STATUS / SELECT pg_current_wal_lsn()
- KILL 后立刻检查:QPS 是否回升、连接数是否回落、错误率是否下降;用 pt-query-digest 或 PG 的 pg_stat_statements 验证该SQL是否消失
- 所有操作写入值班日志(含时间、执行人、SQL原文、KILL ID、影响行数估算),禁止口头交接
慢查询应急不是技术炫技,是稳准狠的外科手术——止血比缝合重要,记录比解释重要,验证比执行重要。