
1. 项目背景与整体设计思路1.1 为什么监控MySQL不能只靠Zabbix自带的模板很多人在搭建Zabbix监控体系时习惯性地把Zabbix-agent装好套用官方自带的Template DB MySQL模板看到绿灯亮起就觉得万事大吉。但实际运维中你会发现官方模板能拿到的指标非常有限——它主要依赖agent的UserParameter去执行一些简单的mysqladmin命令能采集到的无非是连接数、慢查询数量、QPS这些表层数据。一旦遇到InnoDB缓冲池命中率骤降、锁等待堆积、主从延迟飙升这类真正要命的问题官方模板基本是“睁眼瞎”。我在实际项目中踩过这个坑。某次线上数据库响应突然变慢Zabbix面板上一切正常CPU、内存、连接数都在阈值内结果登上去一看InnoDB的history list length已经涨到几十万undo log堆积严重。这件事之后我就下定决心MySQL监控必须做到“内核级”要能直接读到SHOW GLOBAL STATUS和SHOW ENGINE INNODB STATUS里的关键指标。所以这套方案的整体设计思路是Zabbix-agent负责采集自定义脚本负责取数自定义模板负责展示和告警。agent本身只是一个执行器和数据传输通道真正的监控逻辑写在shell脚本里通过UserParameter暴露给agent调用。这样做的好处是灵活——你想监控什么指标改脚本就行不用动agent的配置文件结构同时脚本可以做到权限隔离用一个只读账号去连数据库安全可控。1.2 分布式监控中agent的角色定位在分布式监控架构里Zabbix-agent扮演的是“末梢神经”的角色。它部署在被监控主机上负责本地数据采集然后被动或主动地把数据推给Zabbix-Server。这里有个关键选择被动模式还是主动模式。被动模式下Server定期向agent发起请求拉数据agent监听10050端口等待连接。主动模式下agent自己定期把数据推给Server的10051端口。两者最大的区别在于大规模部署时的性能表现。我管过上百台机器的环境被动模式下Server的pollers进程会被大量并发连接拖垮尤其是网络抖动时一堆连接超时重试Server负载直接起飞。所以只要机器数量超过50台我强烈建议用主动模式。不过主动模式有个坑agent推送数据的频率由RefreshActiveChecks参数控制默认120秒。这意味着你改了监控项之后最长要等2分钟agent才会去Server拉取最新的监控项列表。调试阶段这个等待很折磨人可以临时改成10秒上线后再调回去。1.3 版本选型为什么是Zabbix-agent 5.0配CentOS 7.5Zabbix 5.0是LTS版本支持周期到2025年对于生产环境来说稳定性有保障。相比4.05.0在agent端最大的改进是支持了UserParameter的灵活重载和更完善的主动模式心跳机制。CentOS 7.5虽然现在看有点老但大量存量业务系统还跑在上面glibc版本和systemd的成熟度都经过充分验证不会出现新系统上那种依赖库版本冲突的糟心事。agent 5.0对MySQL的监控能力本身没有内置增强但它对UserParameter的执行超时控制更精细了。默认Timeout是3秒如果你的MySQL监控脚本执行时间较长比如要跑多个SHOW命令需要在agent配置里把这个值调大否则会频繁出现“Timeout while executing”的错误。这个细节后面会详细讲。2. 环境准备与依赖梳理2.1 系统层面的前置检查在动手装agent之前有几项系统状态必须先确认否则后面出问题排查起来会很痛苦。第一确认SELinux的状态。CentOS 7默认开启SELinux而Zabbix-agent的很多操作比如执行自定义脚本、写日志文件会被SELinux拦截。你可以用getenforce查看当前状态。如果是Enforcing要么改成Permissive要么为agent单独配置SELinux策略。生产环境我一般建议改成Permissive因为给agent写策略规则的复杂度远高于收益。第二确认防火墙规则。被动模式下需要放行10050端口主动模式下agent是往外连的不需要开放入站端口。用firewall-cmd --list-ports看一下当前放行了哪些端口。如果用的是iptables用iptables -L -n检查。第三确认时间同步。Zabbix对时间非常敏感Server和agent的时间差超过一定阈值会导致数据被丢弃。用timedatectl status确认NTP同步是否正常。我遇到过一台机器因为时间漂移了15分钟导致所有监控数据在Server端显示为“未来数据”而被拒绝入库排查了半天才发现是chronyd挂了。第四确认磁盘空间。agent本身的日志量不大但如果你开了DebugLevel4以上的调试日志一天能写几百MB。/var/log/zabbix/目录至少留1GB空间。2.2 软件源配置与agent安装Zabbix官方提供了yum源直接配置就行。这里有个细节CentOS 7.5的默认yum源里没有zabbix-agent5.0的包必须手动添加官方repo。rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm yum clean all装完repo包之后/etc/yum.repos.d/下会多出几个zabbix相关的repo文件。默认情况下zabbix.repo里的enabled是1但zabbix-frontend和zabbix-agent的仓库可能需要单独开启。我建议直接编辑/etc/yum.repos.d/zabbix.repo确认[zabbix]段的enabled1然后执行安装yum install -y zabbix-agent安装完成后用rpm -ql zabbix-agent看一下装了哪些文件。关键文件有三个/etc/zabbix/zabbix_agentd.conf是主配置文件/etc/zabbix/zabbix_agentd.d/是自定义配置的存放目录/usr/sbin/zabbix_agentd是二进制程序。我习惯把所有自定义的UserParameter都放在zabbix_agentd.d/下面用独立的.conf文件管理这样升级agent时不会覆盖自己的配置。2.3 MySQL只读监控账号的创建监控脚本需要连MySQL取数据绝对不能直接用root账号。创建一个专用的只读账号权限最小化。CREATE USER zbx_monitorlocalhost IDENTIFIED BY YourStrongPassword123!; GRANT USAGE, PROCESS, REPLICATION CLIENT ON *.* TO zbx_monitorlocalhost; FLUSH PRIVILEGES;这里解释一下为什么是这三个权限USAGE允许账号连接但不给任何实际数据权限这是最小连接权限。PROCESS允许执行SHOW PROCESSLIST和SHOW ENGINE INNODB STATUS这两个命令是监控锁等待和InnoDB状态的核心。REPLICATION CLIENT允许执行SHOW SLAVE STATUS和SHOW MASTER STATUS如果你有主从架构这个权限必须给。注意不要给SELECT权限监控脚本不需要读任何业务表。密码要足够复杂因为agent配置文件里是明文存储的虽然文件权限是640但多一层防护总没错。提示如果你的MySQL是8.0版本创建用户时默认的认证插件是caching_sha2_password而老版本的mysql客户端可能不兼容。可以在创建用户时指定IDENTIFIED WITH mysql_native_password BY 密码来避免连接问题。3. 核心配置与脚本实现3.1 agent主配置文件的调优项/etc/zabbix/zabbix_agentd.conf里有几个参数必须调整默认值在生产环境是不够用的。Server和ServerActiveServer填Zabbix-Server的IP被动模式下用来做白名单ServerActive也填Server的IP主动模式下agent往这个地址推数据。如果Server有多个IP或者走了内网域名都要写进去用逗号分隔。Hostname这个值必须和Zabbix Web端添加主机时的“主机名称”完全一致大小写敏感。我见过有人填了IP地址结果Web端用主机名导致主动模式下数据推上来找不到对应主机全被丢弃。Timeout默认3秒对于MySQL监控脚本来说太短了。SHOW ENGINE INNODB STATUS在繁忙的实例上可能要跑好几秒。建议调到10秒。但注意这个值不能超过30秒否则Server端会先超时。StartAgents被动模式下启动的agent进程数默认3个。如果监控项很多可以适当调大但不要超过10否则内存占用会明显上升。RefreshActiveChecks主动模式下拉取监控项列表的间隔默认120秒。调试阶段改成10上线后改回60或120。UnsafeUserParameters默认是0表示UserParameter的命令里不允许包含某些特殊字符。如果你脚本里用了管道、重定向这些需要改成1。但改之前要评估安全风险确保只有可信的脚本能被执行。3.2 MySQL监控脚本的编写要点脚本放在/etc/zabbix/scripts/目录下我一般命名为mysql_status.sh。核心逻辑是用mysql客户端连数据库执行SHOW命令然后用awk/sed提取需要的值。#!/bin/bash MYSQL_USERzbx_monitor MYSQL_PASSYourStrongPassword123! MYSQL_HOST127.0.0.1 MYSQL_PORT3306 case $1 in uptime) mysql -u$MYSQL_USER -p$MYSQL_PASS -h$MYSQL_HOST -P$MYSQL_PORT -e SHOW GLOBAL STATUS LIKE Uptime 2/dev/null | awk NR2{print $2} ;; threads_connected) mysql -u$MYSQL_USER -p$MYSQL_PASS -h$MYSQL_HOST -P$MYSQL_PORT -e SHOW GLOBAL STATUS LIKE Threads_connected 2/dev/null | awk NR2{print $2} ;; innodb_buffer_pool_hit_rate) # 计算InnoDB缓冲池命中率 reads$(mysql -u$MYSQL_USER -p$MYSQL_PASS -h$MYSQL_HOST -P$MYSQL_PORT -e SHOW GLOBAL STATUS LIKE Innodb_buffer_pool_reads 2/dev/null | awk NR2{print $2}) requests$(mysql -u$MYSQL_USER -p$MYSQL_PASS -h$MYSQL_HOST -P$MYSQL_PORT -e SHOW GLOBAL STATUS LIKE Innodb_buffer_pool_read_requests 2/dev/null | awk NR2{print $2}) if [ $requests -gt 0 ]; then echo scale4; (1 - $reads / $requests) * 100 | bc else echo 100 fi ;; slave_delay) mysql -u$MYSQL_USER -p$MYSQL_PASS -h$MYSQL_HOST -P$MYSQL_PORT -e SHOW SLAVE STATUS\G 2/dev/null | grep Seconds_Behind_Master | awk {print $2} ;; *) echo Unknown parameter: $1 exit 1 ;; esac这个脚本有几个关键点需要注意。第一2/dev/null把mysql客户端的警告信息屏蔽掉否则密码写在命令行会触发“Using a password on the command line interface can be insecure”的警告污染输出。第二awk NR2{print $2}是因为SHOW STATUS的输出第一行是列名第二行才是值。第三命中率的计算用bc做浮点运算shell本身不支持小数。脚本写完后要给执行权限chmod x /etc/zabbix/scripts/mysql_status.sh。然后手动测试一下/etc/zabbix/scripts/mysql_status.sh uptime确认能正常输出数字。3.3 UserParameter的配置与加载在/etc/zabbix/zabbix_agentd.d/下新建mysql_monitor.confUserParametermysql.uptime,/etc/zabbix/scripts/mysql_status.sh uptime UserParametermysql.threads_connected,/etc/zabbix/scripts/mysql_status.sh threads_connected UserParametermysql.innodb_buffer_pool_hit_rate,/etc/zabbix/scripts/mysql_status.sh innodb_buffer_pool_hit_rate UserParametermysql.slave_delay,/etc/zabbix/scripts/mysql_status.sh slave_delay配置文件的格式是UserParameterkey,command。key是你在Zabbix Web端创建监控项时填的键值command是实际执行的命令。注意command里不要有中文和特殊字符路径要写绝对路径。改完配置后重启agentsystemctl restart zabbix-agent。然后用zabbix_get测试zabbix_get -s 127.0.0.1 -k mysql.uptime如果返回数字说明配置生效。如果报“ZBX_NOTSUPPORTED”检查agent日志/var/log/zabbix/zabbix_agentd.log通常是脚本权限问题或者路径写错了。注意每次修改UserParameter配置文件后都必须重启agent因为agent只在启动时读取这些配置。这一点和很多人的直觉不同以为改完文件会自动生效。4. Web端模板配置与告警设置4.1 自定义模板的创建与监控项定义登录Zabbix Web进入“配置”-“模板”点击“创建模板”。模板名称填“Template MySQL Custom Monitor”群组选“Templates/Databases”。创建完模板后在模板下添加监控项。以“MySQL Uptime”为例名称MySQL Uptime键值mysql.uptime信息类型数字无正负单位s更新间隔60s历史数据保留时长7d趋势数据保留时长365d这里有个经验更新间隔不要设得太短。有人为了“实时”把间隔设成10秒结果数据库压力上来了agent也频繁超时。对于Uptime这种变化缓慢的指标60秒足够对于Threads_connected这种波动大的可以设30秒。但无论如何不要低于10秒否则Zabbix的housekeeper进程会被大量历史数据压垮。对于InnoDB缓冲池命中率信息类型选“浮点数”单位填“%”并且要设置“预处理”步骤。因为脚本输出的是类似“99.8500”这样的字符串需要转成浮点数。在“预处理”标签页添加一步“自定义乘数”乘数填1类型选“浮点数”。4.2 触发器与告警阈值的设定监控项只是采集数据真正发现问题靠的是触发器。在模板下创建触发器MySQL服务不可用{Template MySQL Custom Monitor:mysql.uptime.last()}0或者更稳妥的写法用nodata函数nodata(/Template MySQL Custom Monitor/mysql.uptime,300)1表示5分钟内没有收到任何数据就告警。这个比直接判断uptime0更可靠因为如果agent挂了uptime也会变成0但nodata能区分是“服务挂了”还是“agent挂了”。连接数过高{Template MySQL Custom Monitor:mysql.threads_connected.last()}500阈值根据你的max_connections来定一般设成最大连接数的80%。InnoDB缓冲池命中率过低{Template MySQL Custom Monitor:mysql.innodb_buffer_pool_hit_rate.last()}95命中率低于95%说明缓冲池不够大或者有大量全表扫描需要关注。主从延迟过大{Template MySQL Custom Monitor:mysql.slave_delay.last()}300延迟超过300秒告警这个阈值根据业务容忍度调整。触发器的“严重性”要合理设置。Uptime0设成“灾难”连接数过高设成“警告”命中率过低设成“严重”。不要所有触发器都设成最高级别否则告警疲劳真正的问题反而被淹没。4.3 告警媒介与动作配置在“管理”-“媒介类型”里配置告警通道。常见的有邮件、Webhook、短信网关。邮件最简单配置SMTP服务器信息即可。Webhook适合对接企业内部的告警平台。配置完媒介后在“配置”-“动作”里创建触发动作。条件设为“触发器严重性 警告”操作里添加“发送消息”到对应的用户或用户组。消息内容可以用宏来填充比如告警主机{HOST.NAME} 告警时间{EVENT.DATE} {EVENT.TIME} 告警等级{EVENT.SEVERITY} 告警信息{EVENT.NAME} 当前值{ITEM.LASTVALUE}这样收到的告警信息一目了然不用登录Web端就能判断问题严重程度。5. 常见问题排查与避坑经验5.1 agent端常见报错与处理问题一zabbix_get返回ZBX_NOTSUPPORTED这是最常见的错误。排查顺序先看agent日志tail -f /var/log/zabbix/zabbix_agentd.log里面会明确写是“Timeout while executing”还是“Cannot execute command”。如果是Timeout调大Timeout参数如果是Cannot execute检查脚本路径和权限。问题二主动模式下数据推不上去检查ServerActive是否填了正确的Server IPHostname是否和Web端一致。还有一个容易忽略的点Server端的“自动注册”功能是否开启。如果没开自动注册新agent推上来的数据会被拒绝。可以在Server端日志/var/log/zabbix/zabbix_server.log里看到“no active checks”之类的提示。问题三脚本手动执行正常agent调用返回空这是权限问题。agent是以zabbix用户运行的而你的脚本可能依赖某些只有root才能访问的文件或命令。用sudo -u zabbix /etc/zabbix/scripts/mysql_status.sh uptime测试一下如果报权限错误就需要给zabbix用户授权或者把脚本里用到的命令加上sudo免密。5.2 MySQL监控数据的准确性校验监控数据不准比没有监控更可怕。我习惯在配置完监控项后手动跑一遍脚本再登录MySQL执行同样的SHOW命令对比结果是否一致。比如Threads_connected脚本返回50MySQL里SHOW GLOBAL STATUS LIKE Threads_connected也应该是50。如果对不上检查脚本里的awk取值逻辑可能是列的顺序变了或者输出格式有差异。还有一个隐蔽的坑MySQL的status变量是累加值。比如Innodb_buffer_pool_reads从启动到现在一直在累加如果你直接拿这个值做触发器它会一直增大永远触发不了。正确的做法是用Zabbix的change函数或者delta函数来计算增量。我在模板里对这类累加型指标都加了预处理步骤用“简单变化”来取每次采集的差值。5.3 性能与资源占用的平衡监控本身不能成为负担。我见过一个环境agent的StartAgents设了20每个进程都在跑复杂的shell脚本结果被监控主机的CPU有30%被agent吃掉了。几个优化方向第一减少不必要的监控项只采集真正会看的指标第二把多个SHOW命令合并到一个脚本调用里减少mysql客户端的启动次数第三对于变化缓慢的指标把更新间隔从60秒调到300秒第四用zabbix_agentd的BufferSend参数控制主动模式下的发送频率避免频繁建立连接。提示如果你的MySQL实例很多可以考虑用Zabbix的“LLD自动发现”功能让agent自动发现本机上的所有MySQL实例然后批量创建监控项。这样新增实例时不用手动配置省事很多。6. 监控体系的扩展与维护6.1 从单实例到多实例的扩展思路一台机器上跑多个MySQL实例的情况很常见比如3306和3307两个端口。这时候脚本需要支持传入端口参数。改造方法很简单把脚本里的MYSQL_PORT改成从$2读取MYSQL_PORT${2:-3306}然后UserParameter也要相应调整UserParametermysql.uptime[*],/etc/zabbix/scripts/mysql_status.sh uptime $1注意key后面的[*]表示这个key接受参数。在Web端创建监控项时键值填mysql.uptime[3306]和mysql.uptime[3307]就能分别监控两个实例。6.2 模板的版本管理与复用自定义模板一旦调好就要做好版本管理。Zabbix支持模板的导出和导入在“配置”-“模板”页面选中模板点击“导出”可以生成XML文件。把这个XML文件纳入Git管理每次修改都提交一次这样出问题可以快速回滚。跨环境复用时注意模板里引用的UserParameter key必须在目标agent上存在。我一般会把脚本和配置文件一起打包写一个部署脚本自动拷贝文件、设置权限、重启agent减少手动操作的出错概率。6.3 日常巡检与告警调优监控体系上线后不是一劳永逸的。我每周会花半小时做一次巡检看告警历史统计哪些触发器误报最多然后调整阈值看agent日志有没有频繁的超时或连接失败看数据库的监控数据趋势有没有指标在缓慢恶化但还没触发告警。告警调优是个持续过程。初期阈值可以设得宽松一些运行一两周后根据实际数据分布来收紧。比如Threads_connected如果平时峰值也就200那阈值设500就太松了可以调到300。但也不要设得太紧否则业务高峰期频繁告警运维人员会麻木。这套Zabbix-agent加自定义脚本的MySQL监控方案我从5.0版本开始用中间升级过几次agent脚本基本没怎么大改。核心逻辑就是“agent做通道脚本做采集模板做展示”职责清晰维护起来不累。如果你也在用Zabbix管MySQL建议不要偷懒直接用官方模板花半天时间把自定义脚本搭起来后面省下的排查时间远不止这半天。