MySQL 指标报出异常后:从 OpenTelemetry 面板走到 Performance Schema 证据
前三篇把 MySQL 原生遥测导出到 Prometheus、按 DBA 常见问题组织指标、在 Grafana 搭了一个 “MySQL Telemetry Overview” 仪表盘,覆盖六个问题:连接是否被拒、负载是否变化、buffer pool 是否有效服务读、写路径是否有压力、事务是否在等行锁、主机是否参与问题。这一篇讲症状出现时怎么用这张面板。来源:blogs.oracle.com/mysql。
指标面板的作用是发现变化、给出时间点、缩小范围;它不会自动告诉你根因。下一步要回到 MySQL 收集证据:Performance Schema、server status、查询分析,以及平台层(CPU、磁盘、网络)。
症状到证据的对照
| 症状 | 先看指标 | 再收的证据 |
|---|---|---|
| 连接上升或被拒 | threads_connected、errors_max_connections_total、questions_total | max_connections、连接与线程状态、应用连接池行为 |
| 负载上升且响应变慢 | questions_total、threads_running、CPU | 活动语句与语句摘要、慢日志 |
| 读性能变化 | read_requests_total、reads_total、buffer pool 命中率、磁盘读时间 | buffer pool 大小、执行计划与访问模式、工作集变化 |
| 写入变慢 | os_log_written_total、log_waits_total、os_log_pending_fsyncs、磁盘写时间 | redo 与提交行为、存储、写负载与事务大小 |
| 事务等待 | row_lock_current_waits、row_lock_waits_total | data_lock_waits、data_locks、当前语句、事务持有者 |
需求上升开始排队
把 5 分钟窗口的请求速率与 threads_running、CPU 放在同一时间轴上看:
rate(questions_total[5m])
负载上升但 threads_running 和延迟都正常,说明 MySQL 还跟得上。若 threads_running 持续走高,再根据 cache、写路径、锁、主机四块面板决定往哪个方向查。
当前正在执行什么:
SELECT THREAD_ID, EVENT_ID, EVENT_NAME, TIMER_WAIT, SQL_TEXT
FROM performance_schema.events_statements_current
WHERE SQL_TEXT IS NOT NULL
ORDER BY TIMER_WAIT DESC;
看更长周期的负载变化,用 digest 汇总:
SELECT DIGEST_TEXT, COUNT_STAR,
ROUND(SUM_TIMER_WAIT / 1000000000000, 2) AS total_latency_s,
ROUND(AVG_TIMER_WAIT / 1000000000, 2) AS avg_latency_ms,
SUM_ROWS_EXAMINED, QUERY_SAMPLE_TEXT
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT IS NOT NULL
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;
threads_running 高不等于配置有问题:低效 SQL、锁等待、CPU 饱和、存储延迟、需求突变都会给出同一个信号。
行锁等待:指标只说明“有人在等”
指标不会告诉你谁阻塞了谁、锁了什么、SQL 是什么。row_lock_current_waits 表示当前是否存在争用,rate(row_lock_waits_total[5m]) 表示窗口内是否发生过等待事件:
row_lock_current_waits
rate(row_lock_waits_total[5m])
任一指标上升并伴随应用延迟,要立刻抓证据,因为阻塞关系可能在排查开始前就消失了。
先拿阻塞关系:
SELECT REQUESTING_ENGINE_TRANSACTION_ID AS waiting_transaction,
REQUESTING_THREAD_ID AS waiting_thread,
BLOCKING_ENGINE_TRANSACTION_ID AS blocking_transaction,
BLOCKING_THREAD_ID AS blocking_thread,
REQUESTING_ENGINE_LOCK_ID AS waiting_lock,
BLOCKING_ENGINE_LOCK_ID AS blocking_lock
FROM performance_schema.data_lock_waits;
再用线程 ID 取当前 SQL:
SELECT THREAD_ID, EVENT_ID, EVENT_NAME, TIMER_WAIT, SQL_TEXT
FROM performance_schema.events_statements_current
WHERE THREAD_ID IN (/* 等待方与阻塞方的 thread ID */);
把结果拼起来,判断持有会话、语句、表、索引和事务边界。补救方式取决于证据:查询设计、事务范围、并发模式、隔离级别、索引都可能相关。锁等待速率不是“改某个 server 变量”的指令。
常见错误
- 只看一个指标就下结论。
- 把相关当成证明。
- 没确认工作负载或阻塞证据就改配置。
- 没有基线,却用固定阈值判断。
- 等症状结束了才去抓瞬时证据,此时
data_lock_waits和events_statements_current往往已经空了。