MySQL数据库健康检查:如何编写一个真正能用的Shell监控告警脚本
先验证MySQL连接是否存活,再检查主从复制、慢查询和连接数;连接用timeout限制超时,主从需同时监控IO/SQL线程状态及延迟,慢查需调低long_query_time并采样对比,脚本须处理环境变量、权限及退出码。

检查 MySQL 连接是否存活
数据库监控,第一步往往最基础,也最容易被忽略:先确保你能连上。如果连接都建立不了,后续所有关于慢查询、锁表、复制的检查,都成了无源之水。所以,别急着深入,第一步就是确认mysql客户端能否成功访问目标实例。
常见的错误信号非常直接:要么是ERROR 2003 (HY000): Can't connect to MySQL server on 'x.x.x.x' (111),要么就是命令执行后无限期挂起、超时无响应。这类问题通常与SQL本身无关,根源往往在于网络不通、访问权限不足,或者最直接的——mysqld服务进程已经挂了。
- 最简洁的验证命令是:
mysql -h $HOST -P $PORT -u $USER -p$PASS -e "SELECT 1"。为了便于脚本解析,可以加上-s -N参数,去掉表头和边框等格式信息。 - 必须设置超时限制。否则监控脚本可能在网络波动时被长时间卡住,影响后续的检查频率。在Linux下,可以使用
timeout 5 mysql ...;或者直接使用MySQL客户端自带的参数:mysql --connect-timeout=5 ...。 - 安全提醒:在命令行中明文传递密码存在风险。生产环境更推荐的做法是使用
~/.my.cnf配置文件管理凭证,并设置chmod 600确保其权限安全。这样在脚本中只需调用mysql -h $HOST -e "SELECT 1"即可。
判断主从复制是否延迟或中断
监控主从复制,只看SHOW SLA VE STATUS\G输出里的Seconds_Behind_Master这一个数值,是靠不住的。这个值可能为NULL(当IO或SQL线程停止时),也可能长期显示为0,但复制链路实际上已经中断。
真正需要同时盯紧的是三个核心状态:IO线程是否在运行、SQL线程是否在运行,以及复制延迟时间是否超过了预设的阈值(例如60秒)。
- 提取关键字段的命令可以这样写:
mysql -e "SHOW SLA VE STATUS\G" | grep -E "^(Sla ve_IO_Running|Sla ve_SQL_Running|Seconds_Behind_Master):"。 - 如果
Sla ve_IO_Running显示为No,意味着从库无法从主库拉取二进制日志(binlog)。常见原因包括主库宕机、网络中断、主库上的binlog被意外清理,或者复制账号权限不足。 - 一个需要警惕的状态是:
Seconds_Behind_Master为NULL,但Sla ve_SQL_Running却还是Yes。这通常表明IO线程已停止,但SQL线程仍在“消化”之前已获取的日志,属于一种静默故障,需要立即人工介入。 - 即使延迟值显示为
0,也不代表万事大吉。最好结合Exec_Master_Log_Pos(SQL线程执行到的位置)和Read_Master_Log_Pos(IO线程读取到的位置)这两个值是否在持续变化,来判断复制是否真正处于活跃状态。
检测慢查询和连接数突增
连接数被打满,或者慢查询突然堆积,往往是服务即将出现抖动的明确前兆。但监控时不能只看瞬时绝对值,关键是要与历史基线进行对比,并且注意避开那些已知的、正常的定时任务执行窗口。
好消息是,MySQL自身提供的状态变量通常就够用了,一般无需额外安装复杂的监控采集器。
- 当前连接数:执行
mysql -e "SHOW STATUS LIKE 'Threads_connected'"获取。通常,当这个值超过max_connections * 0.8时,就应该触发预警。 - 慢查询数:执行
mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries'"获取。更有效的做法是记录两次采样的差值,如果1分钟内慢查询增长次数超过10(这个阈值可根据业务调整),就值得深入排查。 - 这里有个细节:注意服务器上
long_query_time参数的设置。如果它还是默认的10秒,很多执行时间在1-2秒、但并发量很高的低效SQL就会被漏掉。线上环境通常建议将其调整为1到2秒。 - 避免在业务高峰期直接执行
SHOW PROCESSLIST,因为它可能持有锁。一个更轻量的替代方案是查询信息模式表:SELECT * FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep'。
告警触发与脚本健壮性要点
脚本能在终端里手动跑通,离它能在生产环境稳定运行,中间还差着关键几步。监控脚本必须能自己区分什么是需要告警的“真异常”,什么只是无需理会的瞬时“毛刺”。
- 实现简单的告警降噪:单次检查失败不立即告警,可以设计为连续失败3次再触发邮件或钉钉通知。通常利用一个文件来记录上次成功的时间戳,或者用一个计数器来实现。
- 所有调用
mysql命令的地方,都必须判断其退出码($?)。不能仅仅依赖命令输出的字符串进行匹配,因为网络超时等错误可能根本没有输出。正确做法是:if [ $? -ne 0 ]; then ...。 - 路径问题是个经典陷阱。脚本中使用的
mysql等命令,建议使用绝对路径(例如/usr/bin/mysql)。这可以避免当脚本由crontab等调度工具执行时,因环境变量PATH缺失而找不到命令。 - 日志记录要采用追加模式。推荐使用
echo "$(date '+%F %T') ERROR: ..." >> /var/log/mysql_health.log。切忌使用tee或单一的覆盖重定向(>),否则历史日志会被清空。
最后,最常被忽略的往往是执行上下文和环境问题:crontab默认的PATH变量非常有限;非交互式shell不会加载~/.bashrc中的环境变量;还有SELinux或AppArmor等安全模块可能会阻止脚本读取配置文件。如果不在部署前模拟实际运行环境进行测试,很可能脚本永远静默失败,告警自然也永远不会来。
