18_MySQL 智能巡检方案 ------ 巡检项清单 / 一键巡检脚本 / 健康评分
系列定位 :《MySQL 数据库全栈运维实战系列》第 18 篇,与《Oracle 数据库全栈运维实战系列(VMware 版)》同构对标(Oracle 18 智能巡检)。适用版本 :MySQL 8.0(5.7 差异随文标注)。官方文档:MySQL 8.0 Reference Manual(System Variables / Status Variables / performance_schema)。
一、巡检体系全景

图 1

图 2
| 巡检域 | 核心检查项 | 类比 Oracle 巡检 |
|---|---|---|
| 实例状态 | 实例是否运行、运行时长、版本 | 实例状态 / 启动时间 |
| 连接 | 当前连接数/上限、异常断开 | 会话数/进程数 |
| 性能 | QPS、慢查询、命中率 | AWR 负载 |
| 锁与事务 | 锁等待、长事务、阻塞 | enq 等待 / 阻塞会话 |
| 复制 | IO/SQL 线程、延迟、GTID | DG 状态 / lag |
| 空间 | 库表大小、磁盘占用 | 表空间使用率 |
| 备份 | 最近备份是否成功/新鲜 | 备份巡检 |
| 参数 | 关键参数是否合规 | 参数合规检查 |
二、巡检项清单(检查标准与阈值)
| # | 巡检项 | 健康标准 | 异常判定 | 类比 Oracle |
|---|---|---|---|---|
| 1 | 实例连通 | mysql_up 正常 |
连不上 P0 | 实例 down |
| 2 | 连接数使用率 | < 60% max_connections | > 80% P1 | sessions/limit |
| 3 | 慢查询速率 | 平稳 | 突增 3 倍 P2 | 慢 SQL |
| 4 | Buffer Pool 命中率 | > 99% | < 95% P2 | buffer cache hit |
| 5 | 行锁等待 | 接近 0 | 持续 > 0 P1 | 行锁等待 |
| 6 | 长事务 | 无 > 300s | 存在 P1 | 长事务 |
| 7 | 从库 IO/SQL 线程 | 均 Yes | 任一 No P0 | DG apply/transport |
| 8 | 主从延迟 | < 10s | > 60s P1 | DG lag |
| 9 | 最大表空间 | 增长正常 | 单库 > 磁盘 80% P1 | 表空间 |
| 10 | 备份新鲜度 | 24h 内有成功备份 | > 48h P1 | 备份巡检 |
| 11 | binlog 保留 | 按策略保留 | 磁盘满 P0 | 归档 |
| 12 | 参数合规 | innodb_buffer_pool_size≈60~70%内存 | 偏小 P2 | 参数建议 |
三、一键巡检脚本(可直接复制)
脚本通过 mysql 客户端连库,逐项采集并加权打分。以下为生产可改造的骨架。
#!/bin/bash
# mysql_inspect.sh ------ MySQL 一键巡检(解释:用法 ./mysql_inspect.sh -h 127.0.0.1 -P 3306 -u exporter -p 'xxx')
HOST=127.0.0.1; PORT=3306; USER=exporter; PASS='Exp@Pass2026'
while getopts "h:P:u:p:" opt; do
case $opt in h)HOST=$OPTANT;; P)PORT=$OPTANT;; u)USER=$OPTANT;; p)PASS=$OPTANT;; esac
done
MYSQL="mysql -h$HOST -P$PORT -u$USER -p$PASS -N -B"
SCORE=100
echo "========== MySQL 巡检报告 $HOST:$PORT $(date) =========="
# ① 实例状态(解释:能查到版本即实例正常)
VERSION=$($MYSQL -e "SELECT @@version" 2>/dev/null)
if [ -z "$VERSION" ]; then echo "[P0] 实例无法连接"; SCORE=$((SCORE-40)); else echo "[OK] 实例版本: $VERSION"; fi
# ② 连接数使用率(解释:当前连接 / 上限)
CONN=$($MYSQL -e "SHOW GLOBAL STATUS LIKE 'Threads_connected'" | awk '{print $2}')
MAXCONN=$($MYSQL -e "SHOW VARIABLES LIKE 'max_connections'" | awk '{print $2}')
PCT=$((CONN*100/MAXCONN))
echo "[INFO] 连接数 $CONN / $MAXCONN (${PCT}%)"
if [ $PCT -gt 80 ]; then echo "[P1] 连接数超 80%"; SCORE=$((SCORE-15)); fi
# ③ 运行时长(解释:uptime 突降说明刚重启)
UPTIME=$($MYSQL -e "SHOW GLOBAL STATUS LIKE 'Uptime'" | awk '{print $2}')
echo "[INFO] 运行时长 ${UPTIME} 秒"
if [ $UPTIME -lt 3600 ]; then echo "[P2] 实例运行不足 1 小时(可能刚重启)"; SCORE=$((SCORE-5)); fi
# ④ 慢查询总数(解释:慢查询累计量,结合趋势)
SLOW=$($MYSQL -e "SHOW GLOBAL STATUS LIKE 'Slow_queries'" | awk '{print $2}')
echo "[INFO] 累计慢查询 $SLOW"
# ⑤ 行锁等待(解释:InnoDB 行锁等待次数)
LOCKWAIT=$($MYSQL -e "SHOW GLOBAL STATUS LIKE 'Innodb_row_lock_waits'" | awk '{print $2}')
echo "[INFO] 行锁等待累计 $LOCKWAIT"
# ⑥ 长事务(解释:运行 > 300 秒的事务)
LONGTRX=$($MYSQL -e "SELECT COUNT(*) FROM information_schema.innodb_trx
WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 300")
echo "[INFO] 长事务(>300s) 数量: $LONGTRX"
if [ "$LONGTRX" -gt 0 ]; then echo "[P1] 存在长事务"; SCORE=$((SCORE-15)); fi
# ⑦ 从库复制状态(解释:仅从库有值;主库该查询返回空)
IO_RUN=$($MYSQL -e "SHOW REPLICA STATUS\G" 2>/dev/null | grep "Replica_IO_Running:" | awk '{print $2}')
SQL_RUN=$($MYSQL -e "SHOW REPLICA STATUS\G" 2>/dev/null | grep "Replica_SQL_Running:" | awk '{print $2}')
LAG=$($MYSQL -e "SHOW REPLICA STATUS\G" 2>/dev/null | grep "Seconds_Behind_Source:" | awk '{print $2}')
if [ -n "$IO_RUN" ]; then
echo "[INFO] 从库 IO=$IO_RUN SQL=$SQL_RUN 延迟=${LAG}s"
if [ "$IO_RUN" != "Yes" ] || [ "$SQL_RUN" != "Yes" ]; then echo "[P0] 复制线程异常"; SCORE=$((SCORE-30)); fi
if [ "$LAG" -gt 60 ] 2>/dev/null; then echo "[P1] 主从延迟 >60s"; SCORE=$((SCORE-10)); fi
fi
# ⑧ Buffer Pool 命中率(解释:1 - 物理读/逻辑读,应 > 99%)
BP_READS=$($MYSQL -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'" | awk '{print $2}')
BP_REQS=$($MYSQL -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests'" | awk '{print $2}')
if [ "$BP_REQS" -gt 0 ] 2>/dev/null; then
HIT=$(awk "BEGIN{printf \"%.2f\", (1 - $BP_READS/$BP_REQS)*100}")
echo "[INFO] Buffer Pool 命中率 ${HIT}%"
awk "BEGIN{exit !($HIT < 95)}" && { echo "[P2] Buffer 命中率 < 95%"; SCORE=$((SCORE-8)); }
fi
# ⑨ 磁盘使用率(解释:guest 视角,thin 置备需宿主复核)
DISKUSAGE=$(df -P /var/lib/mysql | awk 'NR==2{print $5}' | tr -d '%')
echo "[INFO] 数据目录磁盘使用率 ${DISKUSAGE}% (guest 视角)"
if [ "$DISKUSAGE" -gt 80 ]; then echo "[P1] 磁盘 > 80%"; SCORE=$((SCORE-12)); fi
if [ "$DISKUSAGE" -gt 90 ]; then echo "[P0] 磁盘 > 90%"; SCORE=$((SCORE-20)); fi
# ⑩ 备份新鲜度(解释:xtrabackup 最近一次成功备份时间,来自备份标记文件)
BAK_FILE=/backup/last_success
if [ -f "$BAK_FILE" ]; then
BAK_TS=$(cat "$BAK_FILE")
BAK_AGE=$(( $(date +%s) - BAK_TS ))
BAK_HOURS=$(( BAK_AGE/3600 ))
echo "[INFO] 最近一次成功备份距今 ${BAK_HOURS} 小时"
if [ "$BAK_HOURS" -gt 48 ]; then echo "[P1] 备份超过 48h"; SCORE=$((SCORE-12)); fi
else
echo "[P2] 未找到备份成功标记文件"; SCORE=$((SCORE-8))
fi
# ⑪ 表空间大小 Top(解释:按库统计数据目录占用)
echo "--- Top 库占用 ---"
$MYSQL -e "SELECT table_schema,
ROUND(SUM(data_length+index_length)/1024/1024,1) AS MB
FROM information_schema.tables
GROUP BY table_schema ORDER BY MB DESC LIMIT 5;"
# ⑫ 评分等级(解释:按总分映射等级与处置动作)
if [ $SCORE -ge 90 ]; then GRADE="健康(例行)"
elif [ $SCORE -ge 75 ]; then GRADE="良好(关注趋势)"
elif [ $SCORE -ge 60 ]; then GRADE="警告(定位扣分项)"
else GRADE="危险(立即介入)"; fi
[ $SCORE -lt 0 ] && SCORE=0
echo "========== 健康评分: $SCORE / 100 等级: $GRADE =========="
# 运行巡检(解释:输出报告并可邮件/Webhook 推送)
chmod +x mysql_inspect.sh
./mysql_inspect.sh -h 192.168.10.51 -P 3306 -u exporter -p 'Exp@Pass2026' > inspect_$(date +%F).log
5.7 差异 :5.7 用
SHOW SLAVE STATUS,字段为Slave_IO_Running/Slave_SQL_Running/Seconds_Behind_Master;8.0 改为SHOW REPLICA STATUS与Replica_*命名。脚本可按@@version分支兼容。
四、健康评分模型

图 3

图 4
| 分数区间 | 等级 | 处置 |
|---|---|---|
| 90~100 | 健康 | 例行 |
| 75~89 | 良好 | 关注趋势 |
| 60~74 | 警告 | 定位扣分项 |
| < 60 | 危险 | 立即介入 |
4.1 评分细则表(扣分矩阵)
| 检查项 | 触发条件 | 扣分 | 级别 |
|---|---|---|---|
| 实例连通 | 连不上 | -40 | P0 |
| 复制线程 | IO 或 SQL 线程非 Yes | -30 | P0 |
| 磁盘使用率 | > 90% / > 80% | -20 / -12 | P0/P1 |
| 连接数使用率 | > 80% max_connections | -15 | P1 |
| 长事务 | 存在 > 300s 事务 | -15 | P1 |
| 主从延迟 | > 60s | -10 | P1 |
| 备份新鲜度 | > 48h 无成功备份 | -12 | P1 |
| Buffer 命中率 | < 95% | -8 | P2 |
| 备份标记缺失 | 无 last_success 文件 | -8 | P2 |
| 运行时长 | < 1h(疑似刚重启) | -5 | P2 |
评分规则 :基础分 100,逐项累加扣分,扣到 0 为止(不出现负分)。同一项只扣一次(脚本里
if单分支),避免重复扣分;多类隐患叠加时分数快速跌破警戒线,正好反映真实风险。
五、巡检结果落库与上报 Prometheus

图 5
巡检不能只"打印到日志",要持久化 + 可视化。两种落地方式:① 写入本地 sqlite/csv 留痕;② 通过 node_exporter 的 textfile collector 暴露成指标,进 Grafana 看板。
5.1 上报为 Prometheus 指标(textfile collector)
# 巡检脚本末尾追加(解释:把健康评分写进 node_exporter 可读的 .prom 文件)
TEXTFILE_DIR=/var/lib/node_exporter/textfile_collector
mkdir -p $TEXTFILE_DIR
cat > $TEXTFILE_DIR/mysql_inspect.prom <<EOF
# HELP mysql_inspect_health_score MySQL 巡检健康评分 0~100
# TYPE mysql_inspect_health_score gauge
mysql_inspect_health_score{instance="$HOST:$PORT"} $SCORE
# HELP mysql_inspect_grade 巡检等级 1=健康 2=良好 3=警告 4=危险
# TYPE mysql_inspect_grade gauge
mysql_inspect_grade{instance="$HOST:$PORT"} $(case $GRADE in *例行*)echo 1;;*趋势*)echo 2;;*扣分*)echo 3;;*)echo 4;;esac)
EOF
# Grafana 看板直接展示巡检评分(解释:低于 75 自动标红)
mysql_inspect_health_score
# 配套告警:评分跌破 75 触发(解释:与第19篇 Alertmanager 衔接)
# mysql_inspect_health_score < 75 for: 1m -> severity P1
5.2 巡检结果落库(csv 留痕,便于回溯趋势)
# 巡检输出追加一行到 csv(解释:字段=时间,实例,评分,等级,关键隐患数)
echo "$(date '+%F %T'),$HOST:$PORT,$SCORE,\"$GRADE\"" >> /var/log/mysql_inspect/history.csv
VMware 注意事项 :textfile collector 的
.prom目录要和 node_exporter 的--collector.textfile.directory参数指向一致;巡检脚本与 node_exporter 跑在同一虚机,注意文件属主权限(node_exporter 进程要能读该目录)。
VMware 注意事项:巡检脚本若跑在虚机内,注意 ① 巡检用只读账号,避免误操作写库;② 长事务、锁等待查询会短暂加元数据锁,建议低峰执行;③ 磁盘类巡检(df)需在虚机宿主视角评估,thin 置备的虚机磁盘实际增长可能滞后于 guest 内显示,巡检报告应注明"guest 视角"。
六、定时巡检(与第 19 篇衔接)

图 6
# crontab 每天 08:00 跑一次巡检并归档(解释:定时触发,报告推送见第19篇)
0 8 * * * /opt/scripts/mysql_inspect.sh -h192.168.10.51 -uexporter -p'Exp@Pass2026' \
>> /var/log/mysql_inspect/$(date +\%F).log 2>&1
七、要点速记
- 巡检八域:实例/连接/性能/锁事务/复制/空间/备份/参数。
- 一键脚本 :用只读账号连库,逐条
SHOW GLOBAL STATUS+information_schema采集。 - 健康评分:P0 扣 30~40、P1 扣 10~15、P2 扣 5,区间分级处置。
- 重点盯长事务与复制线程:这两类是生产最常见的隐患源。
- 定时化:cron 每日巡检,报告推送接第 19 篇 Webhook/告警。
八、踩坑记录(实测汇总)
| # | 坑 | 现象 | 解决 |
|---|---|---|---|
| 1 | 巡检用高权限账号 | 误操作写库 | 只读账号 exporter |
| 2 | 锁/长事务查询高峰跑 | 短暂元数据锁 | 低峰执行 |
| 3 | 5.7/8.0 字段名混用 | 脚本取不到复制值 | 按版本分支 SHOW SLAVE/REPLICA |
| 4 | uptime 突降误判 | 判为重启 | 结合错误日志确认是否真重启 |
| 5 | thin 置备磁盘 | guest 内空间显示滞后 | 巡检注明 guest 视角,宿主侧复核 |
| 6 | 连接数只看当前值 | 漏掉瞬时峰值 | 配合 Prometheus 历史曲线 |
| 7 | 长事务阈值过大 | 漏报 | 按业务设 300s 内 |
| 8 | 备份巡检只查存在 | 备份可不可恢复未知 | 定期恢复演练(第07篇) |
| 9 | 报告没人看 | 巡检流于形式 | 评分 <75 自动升级(第19篇) |
| 10 | 脚本硬编码密码 | 泄露 | 用配置文件/环境变量 |
九、参考资料(官方正式文档)
| 主题 | 官方链接 |
|---|---|
| MySQL 8.0 Reference Manual | https://dev.mysql.com/doc/refman/8.0/en/ |
| Server Status Variables | https://dev.mysql.com/doc/refman/8.0/en/server-status-variables.html |
| information_schema InnoDB Trx | https://dev.mysql.com/doc/refman/8.0/en/information-schema-innodb-trx-table.html |
| Replication 状态视图 | https://dev.mysql.com/doc/refman/8.0/en/replication-monitoring.html |
| performance_schema | https://dev.mysql.com/doc/refman/8.0/en/performance-schema.html |
本文章属于《MySQL 数据库全栈运维实战系列》监控与巡检篇。欢迎收藏、交流。