ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Zabbix poller进程CPU 100%排查:5大常见原因与调优指南

2026/10/3 1:32:46 拓冰建站 浏览量
Zabbix poller进程CPU 100%排查:5大常见原因与调优指南 1. 问题现象与排查思路poller进程怎么就满了先描述一下我这次遇到的具体场景。监控平台用的是Zabbix 6.0服务器配置不算差4核8G监控的主机数量在600台左右监控项总量大概2.8万个。正常情况下Zabbix Server的poller进程数量是配置好的默认是5个我这边调到了16个。某天早上收到告警说Zabbix Server自身不可达登录后台一看ps -ef | grep zabbix显示所有poller进程的CPU占用率都顶到了100%而且是在好几个poller进程上同时发生。这个现象最让人困惑的地方在于不是某个poller进程满了而是多个poller进程同时打满。按我之前的经验单个poller打满多半是遇到了某个慢查询或卡死的采集项但所有poller一起打满说明问题出在公共资源上而不是单个监控项。先说一下poller进程的工作机制。Zabbix Server核心组件里poller负责主动采集、被动采集和SNMP采集等常规监控项。它的工作模式是从配置缓存里取出待采集的监控项然后发起采集请求等待响应写入历史数据缓冲。整个流程是单线程的顺序执行也就是说一个poller进程同时只能处理一个采集任务。如果某一个采集任务特别慢比如SNMP超时要等3秒这个poller就被占住了其他等待中的任务只能排队。所以排查poller 100%占用的第一个动作不是去看日志而是先确认是哪种“满”。我遇到过几种情况有的是CPU真的打满有的是进程状态卡在D状态不可中断睡眠有的是poller数量配置不合理导致任务堆积还有一种是自己把自己搞死的锁竞争。接下来把这5种常见原因一个一个拆开讲每一种都给出排查命令和解决办法。2. 原因一慢查询与历史数据清理任务冲突2.1 问题描述这是最常见的一种。Zabbix的poller进程CPU跑满表象是CPU占用率高但根源却是在数据库端。我这次遇到的情况就是这样poller进程一个个CPU飙到100%gdb抓堆栈发现全部卡在了数据库查询等待上。进一步排查数据库发现zabbix库里有一张大表history数据量已经到了几亿行而housekeeper进程正在执行历史数据清理。housekeeper是Zabbix负责清理过期数据的进程它删除历史数据的时候如果表数据量巨大会产生大量的DELETE操作。这些DELETE操作对InnoDB来说代价很高不只是删行本身还有索引维护、undo日志、binlog写入、io刷盘。更麻烦的是DELETE操作会持有行锁和间隙锁SELECT类型的查询如果走的是相同范围的索引就会被阻塞住。poller采集完数据后要往history表写数据写操作和删操作在同一个表上互斥这就导致poller进程一直等待数据库锁释放。2.2 排查方法第一步查看数据库慢查询日志确认是否存在大量慢SQL。-- 看当前正在执行的查询 SELECT * FROM information_schema.processlist WHERE command ! Sleep ORDER BY time DESC; -- 看history表的数据量和大小 SELECT table_name, table_rows, ROUND(data_length/1024/1024, 2) AS data_mb FROM information_schema.tables WHERE table_schema zabbix AND table_name LIKE history%;第二步确认housekeeper是否卡住或执行时间过长。查看Zabbix Server日志搜索housekeeper相关记录grep -i housekeeper /var/log/zabbix/zabbix_server.log | tail -20正常情况housekeeper在每次清理周期内的执行时间不会太长如果发现housekeeper [deleted ... records in ... seconds]这行日志里的seconds数值异常大基本可以锁定问题。2.3 解决方案这里分两步走一步是应急一步是根治。应急做法先手动停止housekeeper进程或者直接把数据库里的历史数据快速清理掉一部分。快速清理不要用DELETE效率太低直接按时间范围分批导到临时表再切换表名。比如保留最近7天的数据CREATE TABLE history_new LIKE history; INSERT INTO history_new SELECT * FROM history WHERE clock UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 7 DAY)); RENAME TABLE history TO history_old, history_new TO history; DROP TABLE history_old;这种操作在几亿行的大表上也很快因为INSERT SELECT走的是顺序扫描加批量插入比DELETE一条条删高效得多。但要注意这个操作会有锁表风险建议在业务低峰期做。做完之后把history表的索引重建一下。根治做法调整Zabbix的历史数据保留策略。进前端管理界面选择“管理” - “一般” - “历史数据保留”把趋势数据保留时间缩短或者将历史数据保留时长调到一个合理的值。我的做法是把秒级历史数据保留7天趋势数据保留30天然后把housekeeper的删除频率从默认值调低减少高峰期的锁竞争。另外还有一个关键点Zabbix 6.0之后可以配置历史数据的表分区TimescaleDB插件如果用的是TimescaleDB这个问题基本能从根源上规避。生产环境如果数据量长期在亿级建议在部署阶段就规划好表分区别等到卡了才来救火。注意清理history大表前务必确认磁盘空间足够临时表会占用额外的磁盘空间建议预留原表大小50%以上的可用磁盘。2.4 实操心得我见过不少运维同行在poller打满时第一时间去看Zabbix的采集配置怀疑是某个监控项卡住了其实方向搞偏了。只要发现多个poller进程同时CPU 100%且数据库端有大量耗时查询优先查数据库层面99%是历史表清理问题。单个poller卡住的场景才更应该怀疑具体监控项配置。3. 原因二SNMP采集超时和不合理的重试策略3.1 问题描述第二种常见原因是SNMP采集相关。做过网络设备监控的人应该有印象网络设备响应慢是很常见的事情尤其是跨公网、跨运营商的链路丢包和延迟波动都很明显。如果监控项里配置了SNMP协议采集默认超时时间是3秒重试次数是1次。一个SNMP监控项在最坏情况下会占用poller进程6秒以上3秒超时3秒重试。如果被监控的设备数量多且都是网络设备比如交换机、路由器、防火墙那么一种常见的情况是某个网段出现网络波动批量设备都响应超时所有poller进程就像被同时点穴一样全部堵在等待响应的状态。虽然CPU占用率未必真的到100%但poller进程全部被占用新的采集任务无法及时处理表现出来的结果跟CPU打满是一样的。还有更隐蔽的一种情况某个监控项配置的SNMP OID指向了一个不存在的节点或者设备的SNMP community字符串配置错误。设备端在收到SNMP请求时会尝试解析并响应但如果OID不存在有些设备会返回超时而不是立即返回错误这个过程会拖住poller。3.2 排查方法排查SNMP相关问题时建议用命令行模拟一次采集看看设备响应时间snmpwalk -v 2c -c public -t 3 -r 1 192.168.1.1 .1.3.6.1.2.1.1.1.0 snmpget -v 2c -c public -t 3 -r 1 192.168.1.1 sysUpTime.0对比不同设备的响应时间重点排查响应时间在2.5秒以上的设备。接着看Zabbix Server日志中的超时记录grep timed out /var/log/zabbix/zabbix_server.log | tail -50也可以直接查数据库统计哪些监控项的超时次数最多SELECT itemid, COUNT(*) AS timeout_count FROM alerts WHERE alerttype 1 AND status 0 GROUP BY itemid ORDER BY timeout_count DESC LIMIT 20;3.3 解决方案第一个要做的调整是检查全局SNMP超时参数。Zabbix Server配置文件zabbix_server.conf里有几个关键参数SNMPTimeout3s SNMPRetries1这是默认值如果网络环境本身延迟高可以适当调大到5秒重试次数保持1次甚至0次。这里有个权衡超时时间设置太短正常的慢设备可能会被误判为超时造成数据缺失设置太长一旦网络抖动poller会被拖住更久。经验值建议内网设备2-3秒跨公网设备5秒重试次数不建议超过1次。第二个要做的是按设备重要性分离采集配置。高优先级的设备单独建一个主机组用独立的采集模板设置更短超时、更低重试普通设备用另一个模板超时可以适当放大。这样重要的设备不会被慢速网络设备拖累。第三个建议是用SNMP批量采集替代逐个采集。Zabbix原生支持SNMP walk但在4.0之前版本里效率不高。Zabbix 5.0之后支持了SNMP的批量采集方式可以将同主机的多个SNMP监控项合并到一次请求中。具体做法是在模板里配置带有多个OID的“SNMP agent”类型的监控项原型而不是每个OID单独建一个监控项。注意SNMP超时设置改动后要观察至少一个采集周期确认没有被误杀的正常监控项。3.4 常见问题与避坑有人会问SNMP慢查询能不能靠增加poller进程数量来缓解能缓解一部分但治标不治本。增加poller进程相当于增加人手但问题根源是设备响应慢人手再多也得等。真正要解决的是减少慢请求、缩短等待时间以及把慢设备的采集拆到单独的proceess里。Zabbix 5.0及以上版本有两个进程类型值得单独配置一个是pollers一个是ipmi pollers还有snmp trapper。如果SNMP占大头可以考虑创建多个非默认的poller实例配置不同的采集类型不过这个操作比较进阶建议先用上面的超时和模板优化方案。4. 原因三缓存配置过低导致的锁等待与配置同步风暴4.1 问题描述第三种原因比较隐蔽但也比较常见特别是监控规模起来之后。Zabbix Server有个“配置缓存”的概念它是内存里保存监控项、触发器、主机等配置信息的数据结构。采集任务执行前poller进程要从这个缓存里读取配置如果缓存空间不足poller就要等待配置缓存重新分配。在Zabbix前端管理界面查看“报告” - “系统信息”能看到一个关键指标Configuration cache, % used。如果这个值长期超过80%甚至到了90%以上说明配置缓存已经逼近上限。此时一旦有配置变更比如批量导入模板、添加新主机、修改触发器表达式Zabbix Server需要重新构建配置缓存这个过程会锁住配置读取所有poller进程都在等待新配置加载完成。如果配置数据量大缓存重建可能持续几十秒甚至几分钟期间poller进程CPU占用会突然飙升。还有一个关联问题是“配置同步风暴”。当监控规模较大且配置频繁变动时Zabbix Server每个配置同步周期都会把完整的配置缓存发送给所有进程。如果这个同步数据量过大同时poller数量又多服务器CPU和内存都会被短暂打满表现出来就是poller进程集体CPU 100%。4.2 排查方法查看配置缓存使用情况SELECT * FROM zabbix.config;比较关键的字段是configuration_cache相关统计或者直接看前端系统信息页面。日志方面搜索以下关键字grep -E configuration cache|config synced|syncing configuration data /var/log/zabbix/zabbix_server.log | tail -50如果看到大量syncing configuration data并且间隔很短说明配置同步频繁。再确认配置缓存参数grep -E ^CacheSize|^StartPollers /etc/zabbix/zabbix_server.conf4.3 解决方案直接调大配置缓存。zabbix_server.conf里有两个关键参数CacheSize128M StartPollers16默认的CacheSize是8M这个值对中小规模环境够用但监控项超过1万个之后就有点紧张了。官方建议每个监控项大约需要3KB缓存空间我这边2.8万个监控项理论上至少需要80M以上直接调到128M甚至256M比较稳妥。需要注意的是修改CacheSize后要重启Zabbix Server进程才会生效。重启前建议先备份配置重启后观察Configuration cache used的百分比有没有下降。另一个思路是拆分采集任务。让不同数据类型的采集任务使用独立的进程比如SNMP的poller单独启动JMX的poller单独启动IPMI的poller单独启动。这样即使其中一个类型的配置缓存或采集任务有问题也不会把所有poller都拖下水。具体配置方式在zabbix_server.conf里StartSNMPPollers4 StartIpmiPollers2 StartJmxPollers2这几个参数分别控制SNMP、IPMI、JMX类型的独立poller进程数量。默认情况下这些参数都是0也就是说SNMP等类型的采集任务会使用通用的poller进程。手动配置后对应的采集任务就会被独立出来。4.4 实操心得我踩过的一个坑是盲目把StartPollers调得很高比如从16调到64结果CPU没降下来反而因为进程间配置同步的竞争更激烈整体性能更差。后来我把监控项按照数据采集频率做了分层高频监控项用了独立的poller低频监控项走通用poller整体负载才降下来。调优的时候一定要边调边看效果不要一次性把参数拉满。5. 原因四监控项栈溢出或循环依赖导致的poller死循环5.1 问题描述第四种原因相对少见但一旦发生就是大问题因为它不是靠调参能解决的而是配置层面的逻辑问题。Zabbix支持监控项之间依赖比如某台主机的“CPU负载”监控项依赖“系统运行时间”监控项因为负载值是系统运行时间基础上计算出来的。依赖关系可以减少重复采集这本是个好设计但如果依赖配置得不合理就会出问题。有一种经典错误是依赖链形成循环A监控项依赖BB依赖CC又依赖A。这种循环依赖在配置阶段未必能立刻暴露因为Zabbix前端不一定能检测出所有循环依赖关系。一旦配置同步完成poller进程在尝试解析这个依赖链的时候可能会陷入死循环最终导致poller进程CPU持续100%。还有一种情况是监控项值预处理的死循环。Zabbix的值预处理功能可以做正则替换、JSONPath提取、JavaScript计算等操作。如果某个监控项的JavaScript预处理脚本写得有BUG比如写了个永不终止的while循环那么这个监控项在预处理阶段就会死循环占用一个poller进程的CPU 100%而且不会结束。5.2 排查方法排查监控项相关问题时首先想办法定位是哪个监控项卡住了poller。可以使用gdb抓取poller进程的堆栈# 首先找到CPU占用高的poller进程PID top -H -p $(pgrep -f zabbix_server: poller | head -1) # 用gdb抓取该进程的线程堆栈需要在同一台服务器上安装gdb gdb -p PID -batch -ex thread apply all bt 2/dev/null | grep -A 20 zabbix或使用更轻量的方法通过查看进程的打开文件描述符来判断ls -l /proc/PID/fd | grep socket如果堆栈里面出现了zbx_preprocess或者value_preprocessing相关的函数基本可以确认卡在值预处理环节。如果出现了dependency相关字样则要怀疑监控项依赖链问题。另外查看Zabbix Server日志搜索preprocessing关键字grep -i preprocessing /var/log/zabbix/zabbix_server.log | tail -305.3 解决方案值预处理死循环的问题解决方式比较直接在Zabbix前端找到对应的监控项把JavaScript预处理脚本删掉或修正然后给这个监控项做一个简单测试确认脚本能在合理时间内返回结果。JavaScript脚本里别写复杂循环数据量大的场景下用正则和JSONPath内置函数就足够不要动不动就上脚本。如果是依赖链死循环需要梳理监控项依赖关系。这个比较麻烦建议直接用数据库查询找出所有依赖关系SELECT i1.hostid, i1.itemid, i1.key_ AS item_key, i2.itemid AS depends_itemid, i2.key_ AS depends_key FROM items i1 JOIN item_dependencies id ON i1.itemid id.itemid JOIN items i2 ON id.depends_on_itemid i2.itemid ORDER BY i1.hostid, i1.itemid;把查询结果导出来画个依赖关系图一眼就能看出有没有形成环。如果有环删掉其中一个依赖或者改成配置成自动采集别依赖其他监控项。5.4 预防建议在创建大量监控项时建议先在测试环境验证依赖逻辑。特别是导入第三方模板时一定要检查模板里的监控项是否带有依赖关系有些第三方模板做得比较粗糙依赖关系可能有问题。我的习惯是导入模板后先让它在测试主机上跑一周确认poller进程稳定、无异常日志再推广到生产环境。6. 原因五数据库连接池耗尽与poller进程阻塞6.1 问题描述第五种原因也是我这次实际踩中的原因数据库连接池耗尽。从表现上看poller进程CPU占用100%只是表象实际上是poller进程在等待获取数据库连接时被阻塞阻塞期间CPU占用计算方式导致了100%的错觉或者等待期间产生了某种竞争导致CPU飙高。Zabbix Server启动时会根据MaxHousekeeperDelete等配置初始化一定数量的数据库连接池。连接池里的连接数默认情况下是动态的但最大连接数受DBMaxConnections参数控制默认值为100。当监控规模较大、采集频率较高或者有大量历史数据需要即时写入数据库时连接池可能被占满新的采集任务无法获取连接poller进程只能阻塞等待。还有一种情况是数据库服务器自身连接数打满。这个在部署Zabbix时容易被忽略特别是数据库和应用共用一台服务器的时候。MySQL或PostgreSQL的连接数到了上限新的连接请求就会被拒绝Zabbix Server内部就会不断重试可能导致日志刷屏、poller进程异常。6.2 排查方法第一看数据库当前连接数-- MySQL SHOW STATUS LIKE Threads_connected; SHOW VARIABLES LIKE max_connections; -- PostgreSQL SELECT count(*) FROM pg_stat_activity; SHOW max_connections;第二看Zabbix Server日志如果出现类似这样的记录说明连接池出了问题grep -E cannot connect to database|connection pool|connection refused /var/log/zabbix/zabbix_server.log | tail -50第三看poller进程的系统调用确认是否阻塞在数据库连接上strace -p PID -e tracenetwork,read,write -f -t -o /tmp/poller_strace.log如果日志里大量出现connect或read的系统调用且没有返回说明阻塞在数据库通信上。6.3 解决方案针对数据库连接池耗尽优先做两件事。第一件事是确认并调大数据库最大连接数。如果用的是MySQL在my.cnf里调整max_connections 300注意MySQL默认最大连接数是151加上Zabbix自身连接池、前端Web端连接、其他业务系统的连接很容易被打满。调整为300后要观察内存占用连接数越多数据库内存开销越大。第二件事是调整Zabbix Server的数据库连接池参数。zabbix_server.conf里DBMaxConnections100这个值表示Zabbix Server最多能建立多少个数据库连接默认是100。如果数据库端允许更多连接可以把Zabbix Server的连接池上限调大到150甚至200。但注意这个值不宜过大因为每个连接都会占用数据库端内存而且连接数过多时数据库切换上下文也会消耗CPU反而可能加重问题。还有一个操作可以立刻释放被占用的连接手动杀掉Zabbix Server与数据库之间长时间空闲的连接不过这个操作要谨慎杀掉正在执行事务的连接可能导致数据异常。保险的做法是先重启数据库连接池将Zabbix Server进程重启一次让所有数据库连接重新初始化。6.4 实操心得我这次遇到的情况跟数据库连接池耗尽有关具体原因是有一张trends表被频繁写入MySQL的写入性能急剧下降导致连接迟迟不能释放连接池被慢慢占满。排查到最后定位到趋势数据保留时间过长trends和trends_uint表数据量巨大写入性能劣化。改成使用TimescaleDB分区并把趋势数据保留时间从90天降到30天问题才彻底解决。所以排查数据库连接池问题时不要只盯着连接池参数本身也要关注是不是数据库侧出现了性能瓶颈只有消除了瓶颈连接池才能恢复健康。7. 排查工具与参数调优建议汇编7.1 快速定位工具集排查poller CPU 100%问题我的常用工具有这么几个第一个是top和htop用来确认哪些poller进程CPU高。top -H -p可以查看单进程的线程CPU占比帮助区分是单线程占满还是多线程竞争。第二个是ps命令可以查看进程状态。如果poller状态是D不可中断睡眠说明卡在IO上大概率是磁盘或数据库问题。如果状态是R运行中说明真的在拼命计算更可能是死循环或CPU密集任务。ps -eo pid,pcpu,pmem,stat,cmd | grep zabbix_server: poller第三个是strace用来跟踪系统调用。它能告诉我们进程阻塞在什么操作上是网络IO还是文件IO还是数据库连接。不过生产环境用strace要小心会显著拖慢进程建议只针对问题进程短暂地跟踪几秒。第四个是gdb加堆栈抓取这是终极手段。前面提过gdb -p PID -batch -ex thread apply all bt输出结果能直接看到进程卡在什么函数上。这个方法在Zabbix官方论坛上也推荐过属于高级排障手段。第五个是查看Zabbix自带日志这个最重要。zabbix_server.log里包含的细节很多比如history syncer、housekeeper、poller各个进程的执行耗时、错误信息多看日志能省很多事。7.2 关键参数参考表整理了一份我在实际调优中常用的参数参考供参考参数默认值我的建议值适用场景StartPollers510-20监控项数量5000时按每500个监控项增加1个poller的基准调整CacheSize8M64M-256M配置缓存大小监控项越多建议越大SNMPTimeout3s3-5s网络环境差或跨公网采集时调整SNMPRetries10-1重试次数建议不超过1避免拖住pollerDBMaxConnections100100-200数据库连接池上限结合数据库max_connections调整MaxHousekeeperDelete5001000-5000housekeeper每次删除记录数数值越大单次清理越重这些参数没有绝对标准要根据实际环境调整。我的习惯是每次只调一个参数观察24小时再调下一个避免多个参数同时变化后无法定位影响源。7.3 进程与线程的判断经验很多第一次排查poller问题的人会分不清“poller进程”和“poller线程”。Zabbix Server是多进程架构StartPollers配置的是进程数每个poller进程是独立的一个进程有独立的PID。但在Linux的top -H视角下每个进程只有一个主线程。监控项采集是串行的一个poller进程同时只能采集一个监控项。理解这一点非常重要因为它解释了为什么单个慢监控项会拖住整个poller进程进而影响该进程负责的所有监控项。调优时永远不要忽略这个串行特性。8. 一次真实排障过程的完整复盘说一下我这次排障的完整过程希望能帮助读者建立一条清晰的排查路径。整个过程耗时约两个小时从发现问题到最终解决。告警触发后我登录服务器第一件事是确认现象top -b -n 1 | grep zabbix_server: poller结果16个poller进程里有14个CPU占用超过了95%状态全部是R。同时zabbix_server.log刷出了大量超时日志。先排除了SNMP超时问题因为SNMP监控项并不多而且日志里没有明显的SNMP超时记录。接着看数据库结果发现history表的查询和写入都特别慢单次INSERT耗时超过2秒。再往下查发现history_uint表已经超过8亿行而housekeeper正在执行清理任务删除速度远跟不上新增速度造成了严重的锁竞争。poller进程全都卡在等待数据库连接或等待锁释放上所以看起来CPU打满其实是阻塞消耗。临时处理是手动执行了一次数据归档把7天前的history_uint数据切到备份表再删除。这个操作过程花了大概10分钟执行完后poller进程逐渐恢复正常。根治措施是启用了TimescaleDB分区调整了保留策略并将housekeeper的清理频率调整到不那么激进。整个过程复盘下来有一个很重要的教训监控系统自身的健康监控必须提前部署。Zabbix Server本机上的CPU、内存、磁盘IO、数据库连接数、配置缓存使用率这些都需要被监控起来。如果这次在poller打满前我就能看到数据库连接数和history表大小的趋势变化就有机会在问题恶化前提前介入不至于等到业务侧告警才被动应对。另外建议在Zabbix里配置“internal”类监控项比如zabbix[process,poller,avg,busy]、zabbix[cache,configuration,pfree]、zabbix[history,index,0,pfree]这些系统内部指标能直观反映自身运行状态。配上触发器告警等于给监控系统装了个体检仪。9. 写在最后的几点体会poller进程CPU 100%这个问题说到底是监控系统性能瓶颈的一种外在体现。不同场景下触发原因可能完全不一样没有一劳永逸的“万能药”只能靠扎实的基础知识和数据化的排查手段来应对。我个人在实际操作中最深的体会是一切排查问题都要从数据出发。遇到poller打满先看Zabbix日志再看数据库状态再分析监控项配置按这个顺序逐步排除。不要一上来就猜测原因更不要盲目调整参数。每次只改一个变量观察效果记录日志这是最稳妥的做法。还有一个经验是监控系统的自我监控能力要提前建设。Zabbix能监控任何东西但很多人都忘了监控Zabbix自己。把poller进程状态、历史数据表大小、数据库连接数、配置缓存使用率纳入监控范围配上合理的告警阈值大部分poller问题都能在影响业务前被提前发现。最后分享一个小技巧在poller高负载时期临时把任务分散到独立poller进程能显著降低整体阻塞风险。但这个方法只适合临时过渡长期方案仍然是优化监控项配置、优化数据库性能、调整保留策略。希望这篇排障总结能帮到正在被poller问题困扰的朋友少走一些弯路。