Files
2026-05-24 11:42:38 +08:00

12 KiB
Raw Permalink Blame History

tags, create time
tags create time
MySQL
监控指标
QPS
TPS
Buffer Pool Hit Rate
Prometheus
Grafana
InnoDB
2026-05-16 00:00

监控指标

概述

有效的监控体系能让你在问题爆发前发现问题。MySQL 的监控涵盖 性能、可用性、容量 和 安全 四个维度。

[!TIP] 核心思想 监控的目的不是「收集更多数据」,而是 「用最少的关键指标发现最多的问题」。初学者常犯的错误是把所有 SHOW STATUS 变量都记录下来——但真正需要告警的可能不到 20 个。先定义你的 SLA/SLO,再决定监控什么。

一个典型的 MySQL 监控栈自下而上:

graph TD
    A["MySQL Server"] -->|"SHOW GLOBAL STATUS / performance_schema"| B["mysqld_exporter"]
    B -->|"HTTP scrape"| C["Prometheus"]
    C -->|"查询 + 告警规则"| D["Grafana"]
    C -->|"Alertmanager"| E["告警通道\n钉钉/飞书/PagerDuty"]
    D -->|"Dashboard"| F["运维 & 开发"]
    
    style A fill:#f9d,stroke:#333
    style C fill:#bee,stroke:#333
    style D fill:#ddf,stroke:#333
    style E fill:#fdb,stroke:#333

理解了这个架构,你就能定位任何一个环节出问题时该检查哪里。

核心指标速查

吞吐量类

指标 SHOW STATUS 名称 健康范围 说明
QPS Queries 视硬件而定 包含内部重写(如 INSERT → multiple row),非客户端请求数
TPS Com_commit + Com_rollback 视硬件而定 只统计 InnoDB 事务型操作
Threads Connected Threads_connected < MaxConnections × 0.8 当前已建立的连接总数
Threads Running Threads_running < CPU 核心数 正在执行查询的线程数(瞬时高峰可短暂超限)

[!WARNING] QPS ≠ 客户端请求数 Queries 计数的是服务器内部处理的查询数,而非客户端发送的请求数。一条 INSERT INTO t VALUES(1),(2),(3) 会被计数为 1 次 Client Query,但可能被优化器展开后计为多次 Internal Queries。如果需要精确的客户端请求量,应看 Com_select + Com_insert + Com_update + Com_delete 的总和。

-- 计算实时 QPS 和 TPS
SHOW GLOBAL STATUS LIKE 'Queries';              -- 累计查询数
SHOW GLOBAL STATUS LIKE 'Com_commit';            -- 累计 commit 数
SHOW GLOBAL STATUS LIKE 'Com_rollback';          -- 累计 rollback 数

-- QPS = Queries / Uptime
-- TPS = (Com_commit + Com_rollback) / Uptime
// Go 中采集指标的推荐方式:直接查询并返回 map
func collectStatus(db *sql.DB) (map[string]string, error) {
    rows, err := db.Query("SHOW GLOBAL STATUS")
    if err != nil {
        return nil, err
    }
    defer rows.Close()

    metrics := make(map[string]string)
    for rows.Next() {
        var name, value string
        rows.Scan(&name, &value) // value 可能是数字或字符串,统一读取为 string
        metrics[name] = value
    }
    return metrics, rows.Err()
}

这段代码的核心思路是 一次查询拿全部指标,避免 N+1 次网络往返。实际生产环境中,你会在每个指标之间做差值运算得到速率,详见下方公式推导。

速率计算公式:

ΔQPS = (Queries_now - Queries_prev) / (Timestamp_now - Timestamp_prev)
ΔTPS = ((Com_commit_now + Com_rollback_now) - (Com_commit_prev + Com_rollback_prev)) / ΔSeconds

Prometheus 的 mysqld_exporter 会自动帮你完成这个差值计算,如果用原生 SQL 采样则需自行实现。

InnoDB 引擎指标

InnoDB 是绝大多数 MySQL 业务场景使用的存储引擎,其内部指标决定了数据库的整体健康状况。

指标 SHOW STATUS 名称 健康阈值 说明
Buffer Pool 读请求 Innodb_buffer_pool_read_requests — 从 Buffer Pool 中读取的逻辑请求总数
Buffer Pool 磁盘读取 Innodb_buffer_pool_reads 命中率 ≥ 99% 需回磁盘读取的次数(未命中)
Redo Log 写入量 Innodb_data_written 突增意味着写入密集 累计写入 Redo Log 的数据字节数
Redo Log flush 等待 Innodb_log_waits 应接近 0 Redo Log 缓冲区满导致刷盘等待
死锁次数 Innodb_deadlocks 不应持续增加 每次死锁回滚一个事务
平均行锁等待 Innodb_row_lock_time_avg < 10ms 行级锁平均等待时间
行锁总等待时间 Innodb_row_lock_time 监控趋势 所有行锁等待累积耗时(ms)
DML 计数 Innodb_rows_deleted/inserted/updated 监控比例变化 各类 DML 操作的累计次数

[!NOTE] 如何判断 Buffer Pool 大小是否合理? 只看命中率是不够的。更可靠的方法是观察 Innodb_buffer_pool_reads 的增长斜率:如果它几乎是一条水平线(单位时间内增长极少),说明当前 Buffer Pool 够用;如果斜率持续走高,就是增大 innodb_buffer_pool_size 的信号。一般建议设为物理内存的 50%~70%。

-- Buffer Pool 命中率(推荐写法)
SELECT 
    (1 - A.value / B.value) * 100 AS hit_rate_pct
FROM (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') A,
     (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') B;

-- 为什么不用 INFORMATION_SCHEMA.INNODB_METRICS?
-- 该表需要额外启用,且部分版本中字段名不一致,推荐使用 global_status 视图

[!QUESTION] 思考 假设你的 Buffer Pool 命中率为 99.5%,但这个值是从服务启动至今累计计算的。如果最近一小时内出现了缓存穿透,累计比率会显示出来吗?
答案:不会——累计值会稀释短期异常。正确的做法是用定时采集的 差值 来计算窗口内的实时命中率。

复制延迟指标

主从复制是 MySQL 高可用的基石。延迟超过容忍度,读写分离就会带来数据一致性问题。

指标 SHOW REPLICA / SLAVE STATUS 字段 告警阈值 说明
Seconds_Behind_Master Seconds_Behind_Master > 5s 警告,> 30s 严重 从库落后主库的秒数(NULL 表示连接断开)
IO Thread Slave_IO_Running (5.7) / Replica_IO_Running (8.0) 必须 = Yes IO 线程负责拉取 binlog
SQL Thread Slave_SQL_Running (5.7) / Replica_SQL_Running (8.0) 必须 = Yes SQL 线程负责重放事件
Relay Log 空间 Relay_Log_Space 突增预警 中继日志积压通常意味着大事务
-- 标准查看方式(MySQL 8.0.22+ 使用 REPLICA 关键字)
SHOW REPLICA STATUS\G

-- MySQL 8.0+: 基于 GTID 的更精确延迟检测
SELECT 
    PROCESSLIST_TIME AS delay_seconds,
    INFO
FROM performance_schema.threads t
JOIN performance_schema.events_stages_current ess 
    ON t.THREAD_ID = ess.THREAD_ID
WHERE NAME = 'stage/replication_sql_waiting_for_master_to_send_event';

[!CAUTION] Seconds_Behind_Master 的陷阱 当从库停止时,这个值会变成 NULL 而非一个巨大的数字。因此告警逻辑应同时检查 Slave_IO_Running = No、Slave_SQL_Running = No 和 Seconds_Behind_Master IS NULL 三种情况。

慢查询趋势

慢查询日志不只是排障工具,更是 长期性能趋势 的风向标。每天慢查询数量的突然增加,往往预示着即将发生的系统性问题。

-- 每日慢查询统计
SELECT DATE_FORMAT(event_time, '%Y-%m-%d') AS day,
       COUNT(*) AS slow_count,
       ROUND(AVG(query_time), 3) AS avg_time,
       ROUND(MAX(query_time), 3) AS max_time
FROM mysql.slow_log
GROUP BY day
ORDER BY day DESC
LIMIT 30;

[!TIP] 实操建议 在生产环境不建议直接查 mysql.slow_log(它是表格式存储,数据量大时性能差)。推荐使用 Percona 的 pt-query-digest,它能聚合相似查询并按类型统计,还能输出 HTML 报告。

一个实用的告警规则设计:

flowchart LR
    A["Slow Query 阈值\nquery_time > 1s"] --> B{"单条超时 > 10s?"}
    B -->|是| C["立即告警\nP1 级别"]
    B -->|否| D{"当日累计 > 昨日 2x?"}
    D -->|是| E["趋势告警\nP2 级别"]
    D -->|否| F["正常记录"]
    
    style C fill:#f99,stroke:#c00
    style E fill:#fd9,stroke:#ca0
    style F fill:#9f9,stroke:#0a0

这种分级告警能帮你区分「偶发异常」和「系统性恶化」,避免告警疲劳。

告警分级策略

好的监控应该告诉你 现在该做什么,而不是让你从一堆告警里自己判断。

级别 触发条件示例 响应时效 通知渠道
P0 致命 Slave_SQL_Running = No、磁盘空间 < 5%、Innodb_deadlocks > 0/min 5 分钟内 电话 + 短信 + IM
P1 严重 延迟 > 30s、Threads_running > CPU×2、QPS 骤降 50%+ 15 分钟内 短信 + IM
P2 警告 延迟 > 5s、命中率 < 95%、慢查询激增 1 小时内 IM 群
P3 提示 Buffer Pool 利用率持续下降、表空间增长过快 当天处理 日报/周报

[!QUESTION] 你的 SLA 是什么? 不同的业务对可用性的要求差异巨大:金融系统可能要求 99.999%,而内部工具 99.9% 就足够了。先明确业务目标,再反过来设计告警阈值。不要盲目套用别人的配置。

监控工具选型

工具 定位 优点 缺点
Prometheus + mysqld_exporter 指标采集 云原生标准、告警丰富 需自建 Grafana Dashboard
PMM (Percona) 完整监控平台 开箱即用、包含 QAN 分析 较重,占用资源
Zabbix 传统监控 成熟稳定、报警灵活 缺少查询级洞察
Datadog/NewRelic SaaS 监控 零运维、可视化优秀 付费、数据出网
阿里云 RDS 监控 云服务内置 免费、深度集成 锁定云厂商

Prometheus + mysqld_exporter 快速搭建

# prometheus.yml 追加
scrape_configs:
  - job_name: 'mysql'
    static_configs:
      - targets: ['localhost:9104']  # mysqld_exporter 端口
# 启动 exporter(MySQL 5.7)
mysqld_exporter \
  --config.my-cnf=/etc/mysql/debian.cnf \
  --collect.global_status \
  --collect.engine_innodb_status \
  --collect.perf_schema.tableiowaits

# MySQL 8.0 推荐额外开启 perf_schema 指标
mysqld_exporter \
  --config.my-cnf=/etc/mysql/debian.cnf \
  --collect.global_status \
  --collect.info_schema.innodb_metrics \
  --collect.perf_schema.eventswaitssummary

[!NOTE] mysqld_exporter 权限 exporter 需要专门的只读账号,至少授予 REPLICATION CLIENT, PROCESS, SUPER 权限即可。切勿用 root 账号运行。

Grafana 关键 Dashboard

以下面板组合作为基线监控,可根据自身需求增减:

1. Overview: QPS, TPS, Connections, Slow Queries
2. InnoDB Buffer Pool: Hit Rate, Dirty Pages, Free Pages
3. Replication: Seconds Behind Master, IO/SQL Thread Lag
4. Disk I/O: Read/Write Latency, Throughput
5. Network: Bytes Sent/Received per Second
6. Memory: Used vs Available

社区推荐的 Dashboard ID(导入到 Grafana 即可):

主题 Dashboard ID 作者
MySQL Overview 3131 Google
Percona MySQL 9967 Percona
mysqld_exporter 12234 Prometheus Community

关联笔记