跳转至

数据库监控实战:PostgreSQL / MySQL 生产监控四类故障决策树与 5 个真实事故复盘

凌晨 1 点 47 分,我被监控电话叫醒。业务值班说接口大面积超时,P99 延迟从 80ms 飙到 4 秒。我爬起来看 Grafana:主机 CPU 才 15%,内存 40%,磁盘 IO 也正常,OS 监控全绿。按"主机没挂"的经验判断,我差点让值班重启网关再观察。幸好多看了一眼数据库面板——pg_stat_activity 里 300 多个连接全是 idle in transaction,max_connections 已经打满 96%。数据库"假死"了:进程活着,但所有新请求都卡在连接池门口进不去。那次事故之后我明白了一件事:监控数据库 ≠ 监控主机。这篇文把我线上攒的经验写全——怎么用 postgres_exporter + mysqld_exporter 把数据库层盯住,四类故障怎么判断,以及我踩过的 5 个真实坑。想先补 Prometheus 部署和查询基础的,看我之前写的 Prometheus 部署 和 PromQL 查询。

为什么监控数据库 ≠ 监控主机

数据库是有状态的。主机监控看的是"资源用了多少",数据库监控要看"请求排队了多久"。资源低不代表健康——连接池耗尽、锁等待、IO 等待这些瓶颈恰恰不体现在 CPU 上,主机面板全绿,数据库可能已经瘫了。

我遇到的"假死"比"硬宕机"危险得多。硬宕机进程挂了,连接直接报错,应用能感知、能重试;假死是进程活着、端口开着、连接能建上,但每个请求都堵着等锁或等连接,表现为大面积超时——应用以为网络问题,网关以为下游慢,没人怀疑数据库。等到真查出是连接池,业务已经抖了半小时。

所以数据库监控要盯四个层面,缺一不可:

层面 主机监控能发现吗 为什么要单独盯
连接 不能 连接池耗尽是"假死"主因,CPU/内存正常
锁 不能 锁等待是慢查询的隐形推手,CPU 不高
复制 不能 备库延迟会导致主备切换丢数据,OS 全绿
慢查询 不能 慢查询堆积会拖垮连接和锁,指标滞后

链路架构:数据库 → exporter → Prometheus

生产环境我推荐一套干净的链路:exporter 用 systemd 托管跑在数据库本机,Prometheus 从 127.0.0.1 抓取(exporter 只绑本机回环,不暴露外网),告警走 Alertmanager,看板用 Grafana。数据库监控的最小权限和端口隔离这两件事,很多教程压根不提,我吃过亏。

exporter 本机 systemd 部署(以 PostgreSQL 为例)

先建监控账号——绝不复用应用账号,更不要用 root/superuser。PostgreSQL 8.x 起内置了 pg_monitor 角色,一行授权搞定:

-- 用超级用户执行一次
CREATE ROLE exporter LOGIN PASSWORD 'Str0ng-P@ss-For-Monitor';
GRANT pg_monitor TO exporter;
-- pg_monitor 自动包含 pg_read_all_stats、pg_read_all_settings、pg_stat_scan_tables
-- 覆盖了 pg_stat_activity、pg_stat_replication、pg_stat_statements、pg_stat_database 的读取

pg_monitor 是 PostgreSQL 10+ 的内置角色,比我早期手动 GRANT SELECT ON pg_stat_* TO exporter 干净得多——不用逐视图授权,升级也不漏。如果是老版本,手动授 pg_stat_activity、pg_stat_replication、pg_stat_database 的 SELECT 即可。

MySQL 建只读监控用户:

CREATE USER 'exporter'@'127.0.0.1' IDENTIFIED BY 'Str0ng-P@ss-For-Monitor';
-- 最小权限:PROCESS 看连接线程,REPLICATION CLIENT 看复制,SELECT 读库
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'127.0.0.1';
FLUSH PRIVILEGES;

坑:别给 MySQL 监控用户 ALL PRIVILEGES。PROCESS + REPLICATION CLIENT + SELECT 已经够 mysqld_exporter 抓所有核心指标,ALL 等于把只读监控账号变成了半个管理员,被拖库时连 binlog 都能读。

systemd unit(/etc/systemd/system/postgres-exporter.service):

[Unit]
Description=Prometheus PostgreSQL Exporter
After=network.target postgresql.service

[Service]
User=prometheus
Group=prometheus
# 坑:环境变量别写进命令行,ps 会泄露密码
Environment=DATA_SOURCE_NAME="postgresql://exporter:Str0ng-P@[email protected]:5432/postgres?sslmode=disable"
# 只绑本机回环,绝不开 0.0.0.0
ExecStart=/usr/local/bin/postgres_exporter --web.listen-address=127.0.0.1:9187 --web.telemetry-path=/metrics
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

MySQL 同理(mysqld_exporter --web.listen-address=127.0.0.1:9104)。坑:exporter 端口绑 0.0.0.0 等于裸奔——我曾见到某司 exporter 开了公网,任何能访问 9104 端口的人都能看到连接数、复制延迟甚至慢查询 SQL,这是数据泄露级隐患。绑 127.0.0.1 或加防火墙限制源 IP。

docker-compose 方式(适合容器化环境,注意网络要 host 才能让 exporter 访问本机数据库):

services:
  postgres-exporter:
    image: prometheuscommunity/postgres-exporter:latest
    network_mode: host        # 坑:必须 host,bridge 模式下连不上宿主机数据库
    environment:
      DATA_SOURCE_NAME: "postgresql://exporter:Str0ng-P@[email protected]:5432/postgres?sslmode=disable"
    command: ["--web.listen-address=127.0.0.1:9187"]
    restart: always

Prometheus 抓取配置(prometheus.yml):

scrape_configs:
  - job_name: postgres
    static_configs:
      - targets: ['127.0.0.1:9187']   # 或实际 IP,如 10.0.1.5:9187
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance
        replacement: pg-prod-01       # 给实例起个可读名,不然全是 IP 不好看
  - job_name: mysql
    static_configs:
      - targets: ['10.0.1.6:9104']
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance
        replacement: mysql-prod-01

PostgreSQL 黄金指标速查

以下是我在 Grafana 面板里常驻的 PG 指标,全部来自 postgres_exporter 默认导出(无需额外参数):

监控目标 指标名 阈值(预警/严重)
活跃连接占比 pg_stat_database_numbackends / 查询 max_connections >80% / >90%
连接等待 pg_stat_database_tup_returned 增长率 结合慢查询看
事务吞吐 pg_stat_database_xact_commit / xact_rollback 速率 环比突增看
缓存命中率 pg_stat_database_blks_hit / (blks_hit+blks_read) <98% 预警
复制延迟 pg_stat_replication_replay_lag_seconds >10s / >60s
慢查询 pg_stat_statements_mean_exec_time_ms(需启用扩展) >500ms 预警
死元组膨胀 pg_stat_user_tables_n_dead_tup / n_live_tup >20% 触发 vacuum
数据库年龄 pg_database_datfrozenxid_age >2 亿 / >4 亿(防事务 ID 回卷)

对应 PromQL(PG):

# 连接池占用率(numbackends / max_connections)
pg_stat_database_numbackends{datname="mydb"} / on() pg_settings_max_connections * 100

# 缓存命中率(低于 98% 说明有全表扫描压力)
sum(rate(pg_stat_database_blks_hit[5m])) / (sum(rate(pg_stat_database_blks_hit[5m])) + sum(rate(pg_stat_database_blks_read[5m]))) * 100

# 复制延迟(秒)——坑:没有 pg_replication_lag_seconds!
pg_stat_replication_replay_lag_seconds

# 死元组比例(vacuum 压力)
pg_stat_user_tables_n_dead_tup / (pg_stat_user_tables_n_live_tup + pg_stat_user_tables_n_dead_tup) * 100

踩坑①:网上大量教程写的 pg_replication_lag_seconds 根本不存在。正确的延迟在 pg_stat_replication 视图里,字段是 replay_lag(备库重放延迟)、flush_lag、write_lag,postgres_exporter 导出为 pg_stat_replication_replay_lag_seconds。我第一次照着某博客配规则,Prometheus 一直 no data,排查半天才发现指标名错了。

踩坑②:pg_stat_statements 不能只 CREATE EXTENSION。必须先改 postgresql.conf:

# postgresql.conf
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all
# 改完必须重启数据库进程,仅 CREATE EXTENSION 不会加载共享库
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

重启后 postgres_exporter 才导出 pg_stat_statements 开头的指标。我见过太多人只建扩展不重启,然后怀疑 exporter 坏了。

MySQL 黄金指标速查

监控目标 指标名 阈值(预警/严重)
连接占用 mysql_global_status_threads_connected / max_connections >80% / >90%
真正执行 mysql_global_status_threads_running > CPU 核数×2 预警
慢查询率 rate(mysql_global_status_slow_queries[5m]) 持续 >0 且增长
行锁等待 mysql_global_status_innodb_row_lock_waits(累计) 增量 >100/s 预警
死锁 无现成计数器,用 row_lock_waits 推断 见下文
复制延迟 mysql_slave_status_seconds_behind_master >10s / >60s
缓冲区命中 mysql_global_status_innodb_buffer_pool_read_requests / reads <98% 预警
临时表 mysql_global_status_created_tmp_tables 速率 突增看磁盘临时表

对应 PromQL(MySQL):

# 连接占用率
mysql_global_status_threads_connected / mysql_global_variables_max_connections * 100

# 真正执行线程(性能瓶颈看这个,不是 connected)
mysql_global_status_threads_running

# 行锁等待速率(累计计数器,算 rate)
rate(mysql_global_status_innodb_row_lock_waits[5m])

# 复制延迟
mysql_slave_status_seconds_behind_master

踩坑③:Threads_connected 和 Threads_running 别混。前者是已建连接总数(含空闲),后者是正在执行语句的连接数。连接池告警看 connected/max_connections,性能瓶颈看 running。有一次我误用 connected 做性能告警,晚上业务高峰 connected 涨到 2000 触发告警,实际 running 才 30——是连接没释放(连接池泄漏),不是数据库忙。反过来,若 running 长期接近 CPU 核数,才是真的算力吃紧。

踩坑④:MySQL 没有 deadlocks_total 这类现成的死锁计数器。判断锁压力用 mysql_global_status_innodb_row_lock_waits(行锁等待累计次数,算 rate 看增量),或用 performance_schema 的 events_waits_summary_global_by_event_name 查 wait/engine/innodb/row_lock。真要定位死锁,直接 SHOW ENGINE INNODB STATUS 看 LATEST DETECTED DEADLOCK 段,别在指标上找不存在的计数器。

四类故障决策树

这是本文的核心。每类故障我按「症状 → 决策 → 命令 → 阈值」排成决策树,值班时照着走能省一半排查时间。

决策树①:连接池耗尽(假死主因)

症状:接口大面积超时,OS CPU/内存正常
  └─> 看 connected/max_connections 比例
        ├─ <80% → 不是连接池,查下一步
        ├─ 80-90% → 预警,检查是否有连接泄漏(idle 连接堆积)
        └─ >90% → 严重!立即排查
              ├─ 决策:找出"谁占着连接不释放"
              ├─ 命令(PG):SELECT state, count(*) FROM pg_stat_activity GROUP BY state ORDER BY 2 DESC;
              │   # 若 idle in transaction 大量堆积 → 应用事务没提交/没回滚,找业务侧
              ├─ 命令(MySQL):SHOW PROCESSLIST;  # 看大量 Sleep 状态的连接 → 连接池没释放
              ├─ 阈值:idle in transaction > max_connections*0.3 视为异常
              └─ 临时缓解:PG 可 pg_terminate_backend 杀掉空闲事务(谨慎,先确认业务方)

连接池耗尽的典型特征是 idle in transaction(PG)或大量 Sleep(MySQL)堆积——连接建了但一直空闲占着。这不是数据库在忙,是应用侧连接管理有 bug,比如事务里做了网络调用没及时提交。修复方向是应用代码,不是加 max_connections(加了只是把问题推迟,池子迟早再满)。

决策树②:锁与死锁

症状:某几张表查询突然极慢,CPU 不高但请求堆积
  └─> 看锁等待指标
        ├─ PG:SELECT blocked, count(*) FROM pg_locks WHERE NOT granted GROUP BY blocked;
        │   # blocked 列显示谁在等锁;也可查 pg_stat_activity 里 wait_event_type='Lock'
        ├─ MySQL:SHOW ENGINE INNODB STATUS;  # 看 TRANSACTIONS 段的锁等待链
        └─ 决策:找到"持锁方",通常是长事务
              ├─ 命令(PG):SELECT pid, state, now()-xact_start AS dur, query FROM pg_stat_activity WHERE state != 'idle' ORDER BY dur DESC LIMIT 10;
              │   # xact_start 很早但还在跑 → 长事务持锁不放
              ├─ 阈值:事务持续时间 > 60s 预警,> 5min 严重
              └─ 缓解:PG 用 pg_cancel_backend(pid) 取消,严重用 pg_terminate_backend(pid)

死锁和锁等待要分清:死锁是互等(A 等 B、B 等 A,数据库会自动回滚一方),锁等待是单方阻塞(A 持锁,B 一直等)。锁等待更危险——它不会自动解除,会越堆越多,直到连接池耗尽,引发连锁"假死"。MySQL 侧死锁会频繁出现在错误日志,PG 侧 pg_locks 里 granted=false 的锁就是等待链。

决策树③:复制延迟

症状:备库延迟告警,主库看起来正常
  └─> 看 replay_lag(PG)/ seconds_behind_master(MySQL)
        ├─ <10s → 正常,不用管
        ├─ 10-60s → 观察,检查是否大事务/大 DDL 在跑
        └─ >60s → 严重,有主备切换丢数据风险
              ├─ 决策:先判断是"真慢"还是"假警"
              ├─ 假警排查:
              │   ├─ 主从时钟不同步 → 用数据库自身延迟,别自己算主从时间差
              │   ├─ 长事务/大 DDL → 主库执行了 ALTER 大表,WAL/relay log 堆积
              │   └─ 网络抖动 → 看 exporter 抓取是否中断过
              ├─ 真慢排查:SELECT * FROM pg_stat_replication;  # 看 state、write/flush/replay_lag
              └─ 缓解:若备库持续跟不上,考虑临时切只读分流,别硬扛

踩坑⑤:复制延迟假警是高频事故。主从时钟不同步(NTP 没配好)、主库跑长事务、网络抖动都会造成瞬时尖峰。我在告警规则里必须加 for: 5m,否则每晚业务高峰都会收到一堆"复制延迟"的假告警。另外别自己用 abs(主时间戳 - 备时间戳) 算延迟——一旦时钟漂移,这个值永远不准,直接用 replay_lag / seconds_behind_master 这类数据库自身计算的延迟。

决策树④:慢查询

症状:接口变慢但连接数正常,锁等待也不多
  └─> 看慢查询指标
        ├─ PG:pg_stat_statements 的 mean_exec_time 排序
        │   SELECT query, mean_exec_time, calls FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;
        ├─ MySQL:slow_queries 速率 + performance_schema
        │   SELECT * FROM sys.schema_unused_indexes;   # 找出没人用的索引
        └─ 决策:定位到具体 SQL
              ├─ 命令(PG):EXPLAIN (ANALYZE, BUFFERS) <慢SQL>;  # 看 Seq Scan 和 buffer 读取
              ├─ 命令(MySQL):EXPLAIN ANALYZE <慢SQL>;  # 8.0 支持
              ├─ 阈值:单条 >500ms 预警,>2s 严重;全表扫描且 rows 大 → 加索引
              └─ 缓解:加索引前先用 EXPLAIN 确认,别盲目加(写入会变慢)

慢查询是"温水煮青蛙"——它不直接报警,但会慢慢吃掉连接和锁,最终演变成前两类故障。所以慢查询监控是预防性的:pg_stat_statements 里 calls 大且 mean_exec_time 高的 SQL,就是定时炸弹。

5 个真实事故复盘

以下是我在运维一线处理过的真实故障,细节经过脱敏。每个都对应上面一类决策树的落地。

事故一:连接池耗尽导致"假死"(对应决策树①)

现象:凌晨接口大面积超时,OS CPU 15% 全绿。根因:某定时任务改了事务边界,在事务里调了外部 HTTP 接口,HTTP 超时 30s 期间连接一直 idle in transaction 占着池子。300 个连接 200 个卡在事务里。指标怎么显示:pg_stat_database_numbackends 涨到 max_connections 的 96%,idle in transaction 占 200+,CPU 完全正常——这就是"假死"。修复:pg_terminate_backend 杀掉空闲事务,业务侧把 HTTP 调用移出事务。防再发:连接池告警阈值 >80% 预警、>90% 严重,且单独对 idle in transaction 计数加告警。

事故二:长事务持锁拖垮整张表(对应决策树②)

现象:某订单表查询从 10ms 涨到 3s,CPU 正常。根因:一个后台脚本 BEGIN 后做批量更新,中途抛异常没回滚,事务挂着持锁 40 分钟,所有对该表的写全部排队。指标怎么显示:pg_locks 里 granted=false 的锁堆积,pg_stat_activity 里一个 xact_start 40 分钟前的连接 state=active。修复:找到该 pid,pg_cancel_backend 取消。防再发:给长事务加告警(xact_start 超过 5 分钟即报警),并让开发把脚本改成 try/finally 保证回滚。

事故三:主从时钟不同步的复制假警(对应决策树③)

现象:复制延迟告警每小时响一次,检查备库实际数据并不落后。根因:主从两台机器 NTP 没配好,时钟漂了 3 秒,而我当时用 abs(主时间戳 - 备时间戳) 算延迟。指标怎么显示:seconds_behind_master 其实为 0,但我自算的"延迟"恒为 3s 左右,刚好卡在告警线上。修复:两台机配 NTP 同步,改成用数据库自身延迟指标。防再发:告警加 for: 5m,所有复制延迟一律用 replay_lag/seconds_behind_master,不用自算时间差。

事故四:全表扫描拖垮缓存命中率(对应决策树④)

现象:某报表接口越来越慢,缓存命中率掉到 92%。根因:一个统计 SQL 没带索引条件,每跑一次全表扫 200 万行,把 buffer pool 冲得全是无效页。指标怎么显示:pg_stat_database_blks_hit / (blks_hit+blks_read) 从 99% 掉到 92%,pg_stat_statements 里该 SQL mean_exec_time 涨到 1.8s。修复:加复合索引,命中率回到 99%。防再发:缓存命中率 <98% 告警,慢查询 TOP 榜每周 review 一次。

事故五:MySQL 行锁等待堆积成雪崩(对应决策树②)

现象:订单服务批量超时,从一条慢 SQL 开始,10 分钟内连坐全库。根因:一个批量更新没有按主键顺序,两条事务互相等待对方已加锁的行,加上 innodb_lock_wait_timeout 默认 50s,等待链越积越长。指标怎么显示:mysql_global_status_innodb_row_lock_waits 的 rate 从个位数飙升到 200+/s,Threads_running 高企但 Threads_connected 正常。修复:杀掉持锁会话,把批量更新改成按主键排序分批执行。防再发:行锁等待 rate >100/s 告警,锁等待链超过 3 层视为异常。

生产级告警规则

以下是我实际在用的告警规则,for 时窗和注释都是踩坑后的产物。

# database-alerts.yml
groups:
  - name: postgresql
    rules:
      # 连接池占用率(PG)
      - alert: PostgreSQLConnPoolHigh
        expr: pg_stat_database_numbackends{datname!~"template.*"} / on() pg_settings_max_connections * 100 > 80
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "PG 连接池占用 {{ $value | printf \"%.1f\" }}%"
          description: "instance={{ $labels.instance }} datname={{ $labels.datname }} 连接池超过 80%,检查 idle in transaction 是否堆积"

      - alert: PostgreSQLConnPoolCritical
        expr: pg_stat_database_numbackends{datname!~"template.*"} / on() pg_settings_max_connections * 100 > 90
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "PG 连接池耗尽 {{ $value | printf \"%.1f\" }}%"
          description: "可能假死!立即查 pg_stat_activity 的 state,杀 idle in transaction"

      # 复制延迟——坑:必须加 for 防假警(时钟/大事务/网络抖动)
      - alert: PostgreSQLReplicationLag
        expr: pg_stat_replication_replay_lag_seconds > 60
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "PG 复制延迟 {{ $value | printf \"%.1f\" }}s"
          description: "备库重放落后 {{ $value }}s,检查主库是否跑大事务/大 DDL,注意主从时钟同步"

      # 缓存命中率低(全表扫描压力)
      - alert: PostgreSQLCacheHitLow
        expr: sum(rate(pg_stat_database_blks_hit[5m])) / (sum(rate(pg_stat_database_blks_hit[5m])) + sum(rate(pg_stat_database_blks_read[5m]))) * 100 < 98
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "PG 缓存命中率 {{ $value | printf \"%.1f\" }}%"
          description: "低于 98% 可能有全表扫描,查 pg_stat_statements 慢查询 TOP"

  - name: mysql
    rules:
      - alert: MySQLConnPoolHigh
        expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections * 100 > 80
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "MySQL 连接占用 {{ $value | printf \"%.1f\" }}%"
          description: "instance={{ $labels.instance }} 连接超 80%,注意 Sleep 连接堆积(连接池泄漏)"

      - alert: MySQLRowLockWaits
        expr: rate(mysql_global_status_innodb_row_lock_waits[5m]) > 100
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "MySQL 行锁等待速率 {{ $value | printf \"%.1f\" }}/s"
          description: "锁压力高,查 SHOW ENGINE INNODB STATUS 的锁等待链,可能演变成雪崩"

      - alert: MySQLReplicationLag
        expr: mysql_slave_status_seconds_behind_master > 60
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "MySQL 复制延迟 {{ $value }}s"
          description: "备库落后 {{ $value }}s,检查大事务/大 DDL/网络,用数据库自身延迟防时钟假警"

坑:for 不是越大越好。我最早把复制延迟的 for 配成 30m,结果一次真故障延迟持续 25 分钟,告警没发出来——等发出来主库已经被切换了。折中下来,复制延迟这类瞬时尖峰多的用 for: 5m,连接池这类真故障的用 for: 1-2m(宁可早报不可漏报)。告警通知配置(webhook、Silence、抑制)看我写的 Alertmanager 告警规则,看板设计看 Grafana 面板。

FAQ

Q1:PostgreSQL 有没有 pg_replication_lag_seconds 这个指标? 没有。这个指标名在网上流传很广,实际查不到。正确延迟在 pg_stat_replication 视图:replay_lag(秒)是主库提交到备库重放完毕的延迟,另有 flush_lag(备库收到并落盘)和 write_lag(备库收到)。postgres_exporter 导出为 pg_stat_replication_replay_lag_seconds。用 \x SELECT * FROM pg_stat_replication; 可看全部字段。

Q2:为什么我 CREATE EXTENSION pg_stat_statements 了却查不到数据? 只建扩展不重启没用。pg_stat_statements 必须在 postgresql.conf 里配 shared_preload_libraries = 'pg_stat_statements',重启数据库进程后才加载共享库,CREATE EXTENSION 只是建视图和函数。重启后确认 SELECT count(*) FROM pg_stat_statements; 有数据,且 postgres_exporter 会导出 pg_stat_statements_* 指标。

Q3:MySQL 的 Threads_connected 和 Threads_running 有什么区别? Threads_connected 是已建立连接总数(含空闲),Threads_running 是真正在执行语句的连接数。连接池告警看 connected/max_connections(>80% 预警 >90% 严重);性能瓶颈看 running——它接近 CPU 核数才算忙。connected 高但 running 低,通常是连接池泄漏或应用没释放连接,要查业务侧。

Q4:MySQL 有没有现成的死锁次数计数器? 没有 deadlocks_total 这类指标。用 mysql_global_status_innodb_row_lock_waits(行锁等待累计,算 rate 看增量)判断锁压力,或用 performance_schema 的 events_waits_summary_global_by_event_name 查 wait/engine/innodb/row_lock。死锁定位用 SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK 段。

Q5:复制延迟老是瞬时报警怎么办? 告警加 for: 5m 防抖。主从时钟不同步、长事务、网络抖动都会造成瞬时尖峰,for 能滤掉。另外别用 abs(主时间戳-备时间戳) 自算延迟——时钟漂移会让它永远不准,用数据库自身的 replay_lag/seconds_behind_master。

相关文章