
SRE 应急止血清单制定针对核心数据库 CPU 飙高至 95% 的标准化降级止血预案在大促保障的实战中每一个 SRE 架构师最不愿意看到、却又必须准备万全的“地狱级故障”莫过于核心关系型数据库如 MySQL / PostgreSQL的 CPU 监控曲线突然笔直冲顶瞬间钉死在95% 甚至 100%。一旦核心主库的 CPU 被打满整个系统的吞吐量会在几十秒内发生断崖式下跌数据库内部的线程并发Threads_running从正常的几十暴增到数千行锁与表锁竞争加剧上游几百个微服务的数据库连接池HikariCP在几秒内被彻底抽干订单中心、支付中心相继瘫痪网关层的 504 错误像海啸一样在监控大屏上蔓延。此时如果现场值班人员慌乱失措做出了“重启数据库”或者“拉上开发慢慢分析 SQL 执行计划”的错误决定系统就彻底没救了——在千万级未提交事务和高并发写入下重启数据库意味着漫长的 Redo Log 崩溃恢复Crash Recovery主库可能需要 20 到 40 分钟才能重新拉起这个时间足以让公司蒙受上千万元的直接资损。SRE 在极端故障面前必须遵循一条冷酷的铁律“止血永远先于排查”。在大促备战阶段必须为数据库 CPU 飙高制定一套不讲情面、按秒级推进的标准化应急降级止血清单Emergency Stop-Bleeding Runbook。黄金三分钟止血四部曲面对 CPU 95% 的濒死状态值班负责人的大脑不能有任何迟疑必须严格按照以下四步组合拳强制压降负载[00:00 - 00:30] ── 证据链固化自动抓取现场活跃连接与慢 SQL 快照 [00:30 - 01:30] ── 暴力斩首执行 pt-kill 批量秒杀堆积的超长读查询 [01:30 - 02:30] ── 业务降级网关层一键切断所有非核心旁路流量穿透 [02:30 - 03:30] ── 入口限流通过数据库代理ProxySQL开启流量硬性配额第一步30 秒内锁定“犯罪现场”在敲下任何清理命令前的 10 秒钟脚本必须第一时间将当前正在运行的活跃线程与 SQL 文本导出归档。如果直接把连接全杀了事后根本无法定位究竟是哪一条恶魔 SQL 拖垮了系统。第二步批量斩首非核心慢查询Kill Long-Running ReadsCPU 被打满通常是因为某条未走索引的复杂查询如大范围报表扫描、全表排序或笛卡尔积占满了所有 CPU 核心。此时必须果断动用武器只针对SELECT慢读查询执行强制 Kill绝对不允许让写事务卡在半中间。第三步业务非核心功能一键熔断通知业务团队通过配置中心Nacos/Apollo或网关一键触发预先准备好的“大促一级降级开关”关闭历史订单翻页查询关闭积分计算与优惠券复杂凑单推荐关闭用户端实时物流轨迹更新。将宝贵的主库算力百分之百留给最核心的“支付扣款”与“订单创建”主链路。第四步代理层硬性限流压制如果 CPU 依然居高不下说明纯粹是写入 QPS 超过了硬件物理极限。必须在中间件或网关层启用令牌桶限流把发往数据库的并发请求强行截断在安全水位以内如限制最大并发 800 QPS宁可让部分用户看到“排队中请稍后再试”也绝不能让数据库彻底挂死。自动化应急止血脚本与 SQL 工具实战为了杜绝在危急关头人工敲错 SQL我们编写了生产级的一键止血自动化脚本#!/usr/bin/env bash # 核心数据库 CPU 飙高紧急止血脚本 # 用法: ./db_emergency_stop_bleeding.sh db_host db_port set -eo pipefail DB_HOST${1:-127.0.0.1} DB_PORT${2:-3306} DB_USERsre_emergency_admin TIMESTAMP$(date %Y%m%d_%H%M%S) SNAPSHOT_DIR/tmp/db_incident_${TIMESTAMP} mkdir -p ${SNAPSHOT_DIR} echo [*] 启动数据库极限止血程序: Host${DB_HOST}, Port${DB_PORT} # 1. 抓取现场证据链快照 echo [*] [00:10s] 正在固化活跃连接与 InnoDB 引擎状态... mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -e SHOW FULL PROCESSLIST; SHOW ENGINE INNODB STATUS\G ${SNAPSHOT_DIR}/db_snapshot_before_kill.log 21 # 2. 自动化识别并批量杀掉运行时间 3 秒的非核心只读 SELECT 语句 echo [*] [00:40s] 正在执行针对慢查询的精准阻断... mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -e SELECT concat(KILL , id, ;) FROM information_schema.processlist WHERE command Query AND time 3 AND info NOT LIKE %INSERT% AND info NOT LIKE %UPDATE% AND user ! system user AND user ! sre_emergency_admin ${SNAPSHOT_DIR}/kill_candidates.sql # 执行安全击杀 if [ -s ${SNAPSHOT_DIR}/kill_candidates.sql ]; then KILL_COUNT$(wc -l ${SNAPSHOT_DIR}/kill_candidates.sql) echo [!] 发现 [${KILL_COUNT}] 条卡死的长查询立即执行强制终止... mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} ${SNAPSHOT_DIR}/kill_candidates.sql || true else echo [*] 未发现明显慢只读长查询可能为高并发短事务积压准备启动代理限流 fi # 3. 查看杀完后的恢复状态 echo [*] [01:10s] 正在验证 CPU 释放情况... mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -e SHOW STATUS LIKE Threads_running; SHOW STATUS LIKE Threads_connected; echo [] 紧急止血动作执行完毕快照已保存在: ${SNAPSHOT_DIR}必须坚守的三条救火红线在执行数据库紧急止血时值班工程师必须坚守三条防线绝对禁止无差别kill -9数据库进程如果盲目在宿主机上把mysqld进程强行杀死内存中尚未落盘的脏页会导致下一次启动经历漫长的崩溃恢复甚至发生表空间损坏风险。除非主库物理硬件彻底烧毁否则止血必须在 SQL 协议层进行。只杀读请求慎重动写事务慢查询通常是由于复杂的聚合报表引发的这类SELECT查询可以直接杀掉对业务只产生一次重试影响。而对于处于事务进行中的UPDATE或INSERT强行 Kill 会引发大量的回滚操作Rollback如果单事务锁定了数十万行数据回滚时的大量 Undo 日志逆向写入甚至会把 I/O 彻底打死。止血后必须保持持续观察 15 分钟杀掉慢查询后CPU 通常会在 10 秒内迅速回落至 30%。但此时绝不能宣布故障解除如果业务网关没有开启限流或降级上游堆积的重试请求会在 1 分钟后再次涌入并重新打垮数据库。必须在完成业务降级、确保流量平稳可控后方可逐步放开限流阀门。一份合格的 SRE 止血清单是团队在无数次真实故障中用惨痛教训换来的保命符。面对最致命的数据库危机唯有冷静、冷酷且按部就班地执行确定性的止血动作才能在万丈深渊的悬崖边缘把系统稳稳地拉回安全地带。