Shell脚本监控MySQL服务可用性:从原理到生产级实践
目录导读
- 为什么需要监控MySQL服务可用性
- 监控核心指标与检测原理
- 基础Shell脚本实现
- 进阶技巧:连接池、超时与重试机制
- 常见问题与问答环节
- 生产环境部署建议
为什么需要监控MySQL服务可用性
在Web应用架构中,MySQL作为关系型数据库的核心组件,其可用性直接决定业务连续性,根据Statista 2024年数据,数据库故障导致的应用中断占全部运维事故的37%以上,传统人工检测方式存在以下痛点:

- 延迟性:手动执行
mysql命令可能错过间歇性故障 - 盲区:无法检测到部分连接池耗尽但进程存活的“半可用”状态
- 成本:7×24小时人工值守不现实
Shell脚本监控的优势在于轻量级、无依赖、可集成,适合作为第一道防线,但需注意:单纯检查进程是否存活(ps -ef | grep mysql)是无效的——进程僵尸化或锁死时,进程依然存在。
监控核心指标与检测原理
1 必须覆盖的四个维度
| 维度 | 检测命令 | 判断标准 |
|---|---|---|
| 进程存活 | pgrep -x mysqld |
返回PID号 |
| 端口监听 | ss -tlnp | grep 3306 |
状态为LISTEN |
| 连接能力 | mysqladmin ping |
返回"mysqld is alive" |
| 查询响应 | SELECT 1 |
1秒内返回结果 |
2 为什么不能只检查端口
一个反常识案例:某电商平台凌晨出现“MySQL连接超时”,检查发现3306端口正常监听,但连接数已达max_connections阈值(默认151),此时新连接被拒绝,但端口依然开放。端口检测只能证明mysqld进程启动,不能证明服务可用。
基础Shell脚本实现
1 最简单的监控脚本(不足)
#!/bin/bash
mysql -u root -e "SELECT 1" &>/dev/null
if [ $? -eq 0 ]; then
echo "MySQL可用"
else
echo "MySQL不可用"
fi
问题:未处理数据库文件损坏、磁盘满导致的假死状态,更致命的是,如果MySQL被攻击后密码被改,此脚本无法正确反馈(因为仍返回0)。
2 生产级脚本(核心功能)
#!/bin/bash
# 配置参数(建议从环境变量读取)
MYSQL_USER="monitor"
MYSQL_PASS="monitor123"
MYSQL_HOST="127.0.0.1"
MYSQL_PORT="3306"
TIMEOUT=5
LOG_FILE="/var/log/monitor/mysql_health.log"
# 检测函数
check_mysql() {
local result
result=$(timeout $TIMEOUT mysqladmin ping -h"$MYSQL_HOST" -P"$MYSQL_PORT" -u"$MYSQL_USER" -p"$MYSQL_PASS" 2>&1)
if echo "$result" | grep -q "alive"; then
return 0
else
# 记录详细错误
echo "$(date '+%Y-%m-%d %H:%M:%S') FAIL - $result" >> $LOG_FILE
return 1
fi
}
# 连续检测3次(避免偶发网络抖动)
FAIL_COUNT=0
for i in {1..3}; do
if check_mysql; then
FAIL_COUNT=0
break
else
((FAIL_COUNT++))
sleep 2
fi
done
# 输出结果(适合被Zabbix/Prometheus解析)
if [ $FAIL_COUNT -ge 3 ]; then
echo "0" # 0表示异常
# 这里可插入告警:curl -X POST http://alert-api/...
else
echo "1" # 1表示正常
fi
关键设计:
timeout命令防止网络卡死导致脚本挂起- 连续3次检测(间隔2秒)过滤抖动
- 标准输出数值化,方便监控系统采集
进阶技巧:连接池、超时与重试机制
1 处理连接池耗尽
当MySQL连接数达到上限时,mysqladmin ping可能报错too many connections,此时需要额外检测:
# 检查当前连接数占比
CONN_PCT=$(mysqladmin -u root -p${PASS} status 2>/dev/null | awk '{print $4}' | tr -d '()')
# 示例输出:Threads: 145 (96% of max)
if [ $(echo "$CONN_PCT" | grep -oP '\d+(?=%)') -gt 90 ]; then
echo "WARNING: 连接数超过90%"
fi
2 自定义超时应对慢查询
如果某条SQL卡死(如SELECT SLEEP(100)),普通检测会等待超时,使用异步检测:
# 并行执行检测,限制总耗时 timeout 5 bash -c ' mysql -u $USER -p$PASS -e "SELECT 1" & MYPID=$! sleep 3 && kill $MYPID 2>/dev/null wait $MYPID ' 2>/dev/null
3 重试机制的幂等性
告警触发后,避免重复通知,使用临时文件记录状态:
STATUS_FILE="/tmp/mysql_ok"
if [ $FAIL_COUNT -ge 3 ]; then
if [ ! -f "$STATUS_FILE" ]; then
echo "触发告警"
touch "$STATUS_FILE"
fi
else
rm -f "$STATUS_FILE" 2>/dev/null
fi
常见问题与问答环节
Q1:为什么用mysqladmin ping而不是mysqli?
A:mysqladmin ping是最底层的心跳检测,不依赖账号权限(只需PROCESS权限),而mysql -e "SELECT 1"需要读取数据字典,在表损坏时可能失败。两者结合使用更安全:先用ping检测进程,再用SELECT 1验证查询能力。
Q2:监控脚本运行后占用资源吗?
A:单次执行消耗约0.01秒CPU时间和2MB内存,但在高并发系统中(如每5秒执行一次),累计影响显著。建议监控频率为30秒-60秒,并配合nice -n 19降低优先级。
Q3:如何监控主从延迟?
A:在从库执行show slave status\G,提取Seconds_Behind_Master,注意当主库异常时,该值为NULL,参考脚本:
SLAVE_STATUS=$(mysql -u $USER -p$PASS -e "SHOW SLAVE STATUS\G" 2>/dev/null)
IO_RUN=$(echo "$SLAVE_STATUS" | grep "Slave_IO_Running:" | awk '{print $2}')
SQL_RUN=$(echo "$SLAVE_STATUS" | grep "Slave_SQL_Running:" | awk '{print $2}')
if [ "$IO_RUN" != "Yes" ] || [ "$SQL_RUN" != "Yes" ]; then
echo "主从同步中断"
fi
Q4:脚本部署到容器环境有什么注意事项?
A:Docker容器中可能缺少mysqladmin工具,建议使用mysql客户端命令:
# 替代mysqladmin ping mysql -u $USER -p$PASS -e "STATUS;" | grep -q "Uptime:"
同时需注意容器内/var/log目录不可写的问题,建议日志输出到标准输出(echo)后由Docker日志驱动采集。
生产环境部署建议
-
权限最小化:创建专用监控账号
CREATE USER 'monitor'@'127.0.0.1' IDENTIFIED BY 'password'; GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'monitor'@'127.0.0.1';
-
集成到系统监控:写入crontab每30秒执行一次
-
-
-
-
root /usr/local/bin/mysql_monitor.sh
-
-
-
-
-
-
-
- root sleep 30 && /usr/local/bin/mysql_monitor.sh
-
-
-
-
告警分级:
- 单次检测失败 → 日志记录(INFO)
- 连续3次检测失败 → 触发告警(CRITICAL)
- 恢复自动清除告警
-
压力测试:使用
sysbench模拟高负载验证脚本稳定性# 模拟100个并发连接 sysbench --threads=100 --time=60 oltp_read_only run # 同时监控脚本是否准确报出异常
延伸阅读:对于需要更高级功能的场景(如自动故障切换、慢查询追踪),建议评估Percona Monitoring and Management(PMM)或自研基于Shell+Python的混合监控方案,基础Shell脚本应作为第一响应层,与高级监控系统形成互补。