Zabbix 7.0 LTS 分区实战:解决 housekeeper 高负载与数据库膨胀 简介面向Zabbix运维人员与监控系统管理员的一份数据库分区优化实操资料聚焦Zabbix 7.0 LTS在MySQL/MariaDB环境下历史记录与趋势表的分区管理帮助解决数据量累积后housekeeper进程繁忙、旧数据删除效率低等性能瓶颈。资源为单个PDF文档压缩包约835KB内容围绕分区脚本的获取与执行、分区过程的定期自动运行、前端保留天数配置及分区天数调整等环节展开并提示备份数据库与大型库执行耗时等注意事项适用于3.0以后各版本。已有357人学习。读者可据此掌握分区脚本的部署思路与维护要点理解DROP分区相较DELETE语句在清理效率上的优势并形成可复用的数据库生命周期管理方案降低长期维护成本。1. Zabbix 7.0 LTS 装完就卡housekeeper 报警背后的分区账Zabbix 7.0 LTS 装完前端能出图主机能采数很多人以为收工了。跑上两三周日志里开始刷Zabbix housekeeper processes more than 75% busy前端图表加载变慢数据库磁盘一天一个样。这不是 Zabbix 本身装错了而是历史记录和趋势表还在用默认的 DELETE 方式清理旧数据表越大删得越慢删得越慢积压越多最后 housekeeper 进程长期高负载。我见过一套两百来台主机的环境history 表涨到几十 GB 之后内务清理几乎跑不完监控数据写入都开始抖动。这篇记录针对的就是这个场景Zabbix 7.0 LTS 已经装好、MySQL 或 MariaDB 已经在跑接下来把历史与趋势表改成按时间分区让旧数据用 DROP PARTITION 走而不是一行行 DELETE。适合刚把 Zabbix 7.0 跑起来、准备长期维护的开发者也适合从旧版本迁过来、发现数据库膨胀速度不对劲的熟手。2. 装前把环境钉死Zabbix 7.0 LTS 与数据库版本对照分区这件事对 Zabbix 版本不挑3.0 之后的表结构基本一致7.0 LTS 同样适用。真正需要钉死的是数据库侧分区脚本依赖存储过程、事件调度器MySQL 和 MariaDB 都支持但版本差异会影响事件调度器的默认状态和information_schema.partitions的字段表现。我一般先把下面这张对照表填清楚再动手避免装到一半发现数据库版本对不上。组件建议版本说明Zabbix Server7.0 LTS前端、Server、数据库 schema 均为 7.0MySQL8.0.x事件调度器默认关闭需手动开MariaDB10.6 及以上事件调度器同样默认关闭分区脚本zbx_db_partitiong.sql社区通用脚本含建分区、删分区、维护过程字符集utf8 / utf8mb4与 Zabbix 库保持一致即可2.1 先确认数据库类型和事件调度器状态动手前先连上数据库把两件事查清楚当前用的是 MySQL 还是 MariaDB事件调度器开没开。命令不复杂但结果决定后面走事件调度器还是 crontab 两条路。# 登录 Zabbix 所在数据库确认版本与事件调度器状态 mysql -u zabbix -p -e SELECT VERSION(); SHOW VARIABLES LIKE event_scheduler;SELECT VERSION()返回的字符串里带 MariaDB 字样就是 MariaDB否则按 MySQL 处理。event_scheduler为 OFF 是常态MySQL 8.0 和 MariaDB 10.6 默认都不开。这里先记下状态第 3 章创建分区过程时不影响第 4 章配置自动执行时才需要它变成 ON。如果这套库还跑着别的业务改event_scheduler要评估一下实在不方便就用 crontab 方案效果一样。2.2 备份与磁盘余量检查分区脚本本身不改数据但会执行ALTER TABLE ... ADD PARTITION大表上这是一次实打实的表结构变更。新装的 Zabbix 可以跳过备份已经在生产跑的库必须先备。我一般用 mysqldump 只导结构加关键表或者直接对整库做一次逻辑备份视磁盘余量而定。# 备份 Zabbix 库输出到带日期的文件 mysqldump -u zabbix -p --single-transaction --routines --triggers zabbix /backup/zabbix_$(date %F).sql # 看下数据库目录所在分区还剩多少空间 df -h /var/lib/mysql--single-transaction保证 InnoDB 表在备份期间不锁表--routines把已有的存储过程一起带走--triggers带上触发器。磁盘余量至少留出当前库大小的 1.5 倍因为ALTER TABLE加分区时 MySQL 可能产生临时文件。这一步别省我踩过一次在测试库上直接跑脚本结果 history 表太大、临时目录写满只能从备份恢复重来。2.3 确认 Zabbix 库中待分区的表清单Zabbix 需要分区的表是固定的七张history、history_uint、history_str、history_text、history_log、trends、trends_uint。先确认这些表都在且当前没有被分区过避免重复执行脚本。-- 查看七张目标表是否存在以及是否已有分区 SELECT TABLE_NAME, PARTITION_NAME FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA zabbix AND TABLE_NAME IN (history,history_uint,history_str,history_text,history_log,trends,trends_uint);如果PARTITION_NAME全是 NULL说明还没分区可以继续。如果已经有p2024...这类分区名说明之前跑过分区脚本不要再重复建过程直接跳到第 4 章检查自动任务即可。这一步花不了一分钟但能避免把已经分好区的表再折腾一遍。3. 导入分区脚本建过程、改保留天数、跑第一次维护分区脚本的核心是四个存储过程partition_create负责建分区partition_drop负责删旧分区partition_verify负责检查表是否已分区partition_maintenance负责按保留天数调度建和删。脚本里还有一个partition_maintenance_all把七张表串起来一次调用。导入脚本只是把过程建进库真正生效要等第一次调用维护过程。3.1 下载并解压分区脚本脚本从社区渠道获取下载后解压出一个zbx_db_partitiong.sql。放到数据库服务器本地目录别放在/tmp下长期留着后面改天数还要再打开。# 下载并解压分区脚本 cd /opt wget http://bestmonitoringtools.com/dl/zbx_db_partitiong.tar.gz tar -zxvf zbx_db_partitiong.tar.gz ls -l zbx_db_partitiong.sql解压后确认文件存在、大小正常。这个脚本是纯 SQL 文本可以用编辑器打开看里面partition_maintenance_all过程写死了七张表的保留天数默认历史 7 天、趋势 365 天。如果你的环境对历史数据保留有合规要求或者磁盘紧张想缩短趋势保留就在导入前改这个文件比导入后再重建过程省事。3.2 按需修改历史与趋势保留天数打开zbx_db_partitiong.sql找到partition_maintenance_all过程定义里面七行CALL partition_maintenance(...)的第三个参数就是保留天数。历史类表默认 7趋势类表默认 365。改的时候注意历史五张表天数要一致趋势两张表天数要一致否则前端展示的周期和实际保留对不上。-- 脚本中 partition_maintenance_all 的片段第三个参数为保留天数 CREATE PROCEDURE partition_maintenance_all(SCHEMA_NAME VARCHAR(32)) BEGIN CALL partition_maintenance(SCHEMA_NAME, history, 7, 24, 3); CALL partition_maintenance(SCHEMA_NAME, history_uint, 7, 24, 3); CALL partition_maintenance(SCHEMA_NAME, history_str, 7, 24, 3); CALL partition_maintenance(SCHEMA_NAME, history_text, 7, 24, 3); CALL partition_maintenance(SCHEMA_NAME, history_log, 7, 24, 3); CALL partition_maintenance(SCHEMA_NAME, trends, 365, 24, 3); CALL partition_maintenance(SCHEMA_NAME, trends_uint, 365, 24, 3); END第三个参数是KEEP_DATA_DAYS第四个HOURLY_INTERVAL表示每个分区覆盖多少小时24 就是按天分区第五个CREATE_NEXT_INTERVALS表示提前建几个未来分区3 就是提前建三天。我一般保持 24 和 3 不动只调保留天数。历史想留 30 天就把五个 7 改成 30趋势想留 400 天就把两个 365 改成 400。改完保存再执行导入。3.3 导入脚本创建存储过程导入命令把 SQL 文件喂给 mysql 客户端指定库名即可。新库上几秒完成大库上如果表已经有大量数据partition_verify首次给表加分区那一步可能跑很久几小时的情况也遇到过别中途 CtrlC。# 将分区过程导入 zabbix 库 mysql -u zabbix -p你的密码 zabbix /opt/zbx_db_partitiong.sql执行完没有任何输出是正常的SQL 文件里没有 SELECT 打印。想确认过程建好了查一下information_schema.routines。-- 确认四个分区过程已创建 SELECT ROUTINE_NAME FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA zabbix AND ROUTINE_NAME LIKE partition%;应该能看到partition_create、partition_drop、partition_maintenance、partition_maintenance_all、partition_verify五个。少一个就说明导入中途报错被吞了重新执行导入并留意终端输出。3.4 手动跑一次维护过程验证分区生效过程建好后先手动调一次partition_maintenance_all让七张表真正加上分区。这一步的输出会列出每张表建了哪些分区、删了哪些分区是判断脚本是否正常工作的第一手依据。# 手动执行一次全表分区维护 mysql -u zabbix -p你的密码 zabbix -e CALL partition_maintenance_all(zabbix);输出里会出现类似partition_create(zabbix,history,p202401150000,...)的行说明分区在逐个创建。跑完用SHOW CREATE TABLE看 history 表应该能看到PARTITION BY RANGE (clock)和若干p开头的分区定义。-- 查看 history 表的分区结构 SHOW CREATE TABLE zabbix.history\G如果输出里还是普通的CREATE TABLE没有PARTITION BY说明partition_verify没生效回头检查脚本是否完整导入、当前用户是否有 ALTER 权限。第一次手动跑通后面交给自动任务才放心。4. 让分区自己跑事件调度器与 crontab 二选一手动跑一次只是把表结构改好旧分区不会自己消失新分区也不会自己长出来。必须配一个每天或每 12 小时执行一次的自动任务调用partition_maintenance_all。两条路MySQL 事件调度器或者系统 crontab。前者在数据库内部调度不依赖系统 cron 服务后者更直观日志好找。选一条就行别两个都配否则同一时间两个任务同时改分区表容易撞锁。4.1 方案一开启事件调度器并创建定时事件事件调度器默认关闭要在 MySQL 或 MariaDB 的配置文件里打开。配置文件位置因发行版而异MariaDB 常见在/etc/mysql/mariadb.conf.d/MySQL 常见在/etc/my.cnf.d/或/etc/mysql/conf.d/。找不到就用 grep 搜。# 搜索包含 [mysqld] 的配置文件 sudo grep --include*.cnf -irl / -e \[mysqld\]找到后在[mysqld]段落下加一行event_scheduler ON保存重启数据库服务。[mysqld] event_scheduler ON# 重启数据库使配置生效 sudo systemctl restart mysql重启后确认状态变成 ON再创建每 12 小时执行一次的事件。-- 确认事件调度器已开启 SHOW VARIABLES LIKE event_scheduler; -- 创建每 12 小时执行一次的分区维护事件 CREATE EVENT zbx_partitioning ON SCHEDULE EVERY 12 HOUR DO CALL partition_maintenance_all(zabbix);EVERY 12 HOUR表示从创建时刻起每 12 小时触发一次DO CALL后面跟要执行的过程。事件创建后不会立刻执行等下一个 12 小时周期。想马上验证可以查information_schema.events看LAST_EXECUTED字段或者干脆先手动调一次过程确认无误。4.2 方案二用 crontab 每天凌晨执行不方便开事件调度器的环境用 crontab 更稳妥。每天凌晨 3:30 执行一次输出重定向到日志文件出问题有据可查。# 编辑 root 的 crontab sudo crontab -e加入下面这一行# 每天 03:30 执行 Zabbix 分区维护日志写入 /tmp 30 03 * * * /usr/bin/mysql -u zabbix -p你的密码 zabbix -e CALL partition_maintenance_all(zabbix); /tmp/CronDBpartitiong.log 21五个时间字段依次是分、时、日、月、周30 03 * * *就是每天 3 点 30 分。追加日志而不是覆盖方便回看历史执行记录。密码写在 crontab 里是明文权限上要保证只有 root 能读 crontab或者改用/etc/mysql/debian.cnf这类受保护的凭据文件。我一般会在上线后第二天去看一眼日志确认有partition_create或partition_drop输出而不是空文件。4.3 前端关闭内务清理并同步保留天数数据库侧分区接管了旧数据删除Zabbix 前端的内务清理就必须关掉历史与趋势的清理否则两边同时删容易出问题。进「管理」→「一般」→「管家」把「历史记录和趋势」下的「开启内部管家」取消勾选同时勾选「覆盖监控项趋势期间」把历史和趋势天数填成与脚本里一致的值。前端配置项设置值对应脚本参数开启内部管家历史与趋势不勾选由分区过程接管覆盖监控项趋势期间勾选使前端周期与脚本一致历史数据存储期7 天与脚本一致history 类 KEEP_DATA_DAYS趋势数据存储期365 天与脚本一致trends 类 KEEP_DATA_DAYS这里最容易翻车的是天数不一致脚本里历史留 7 天前端填了 30 天前端图表想查 15 天前的数据数据库里分区已经删了图就是空的。改脚本天数时前端这两个数字必须同步改反之亦然。5. 避坑与排查分区上线后最容易翻车的五件事分区这套东西原理不复杂翻车基本集中在权限、天数不一致、自动任务没生效这几处。下面五条是我在实际环境里遇到过的按「现象 → 原因 → 解决」记下来照着对能省不少时间。5.1 现象日志报「表没有值..的分区」前端空图原因通常是自动任务没跑起来或者跑了但过程调用的库名、表名不对导致新数据写入时找不到对应时间范围的分区。Zabbix 日志里会出现[Z3005] query failed: [1526] Table has no partition for value ...。解决先手动执行一次CALL partition_maintenance_all(zabbix);看输出是否正常建出未来分区。如果手动能建、自动不建检查事件调度器是否 ON、事件是否创建成功或 crontab 那行是否被注释、路径是否写错。确认后等一个周期再看日志。5.2 现象分区过程导入报错过程只建了一半原因多是当前数据库用户没有CREATE ROUTINE权限或者脚本里的DELIMITER在某些客户端下没被正确识别。用mysql file方式导入一般没问题用图形客户端粘贴执行容易在DELIMITER上出错。解决确认 zabbix 用户有CREATE ROUTINE、ALTER、DROP权限重新用命令行方式导入。导入后查information_schema.routines确认五个过程齐全缺哪个就单独把那段过程抽出来补建。5.3 现象前端还能查很久以前的数据但数据库里已经没分区了原因是前端「管家」里的历史与趋势天数填得比脚本大前端以为数据还在实际分区已经按脚本天数删掉了。表现是图表时间范围拉长后出现断点或空白。解决把前端「历史数据存储期」「趋势数据存储期」改成与脚本KEEP_DATA_DAYS完全一致。改完不需要重启前端刷新即生效。以后调天数脚本和前端两处一起改别只改一边。5.4 现象大表上执行分区脚本卡住数小时以为挂了原因是大表首次ALTER TABLE ... ADD PARTITION需要重建或校验数据几十 GB 的 history 表跑几小时是正常的不是死锁。此时SHOW PROCESSLIST能看到对应 ALTER 语句在跑。解决别中断中断可能留下中间状态。挑业务低峰执行执行前确认磁盘余量足够。实在等不了可以先在从库或测试库上验证脚本生产库安排维护窗口。5.5 现象事件调度器和 crontab 同时配了分区表偶尔报锁等待原因是两个调度同时调用维护过程对同一张表做 ALTER互相等锁。表现是数据库里出现Waiting for table metadata lock。解决二选一删掉多余的那个。用事件调度器就DROP EVENT zbx_partitioning之外不再配 crontab用 crontab 就把事件删掉。保留一个入口日志也只盯一处。6. 装完怎么验证三条命令确认分区真的在滚动分区配完不等于跑通要确认三件事表确实按时间分了区、旧分区在按天消失、新分区在提前生成。我习惯在配置完的第二天做一次完整验证三条命令足够。第一条看分区结构。SHOW CREATE TABLE输出里应该有PARTITION BY RANGE (clock)并且分区名是p加日期时间最早的分区日期应该在保留天数附近。-- 查看 history 表分区定义确认按 clock 范围分区 SHOW CREATE TABLE zabbix.history\G第二条看分区数量和边界。查information_schema.partitions按分区名排序确认最早分区不早于「今天减保留天数」最晚分区不晚于「今天加提前天数」。-- 统计各表当前分区数量与最早最晚分区 SELECT TABLE_NAME, COUNT(*) AS part_count, MIN(PARTITION_NAME) AS first_part, MAX(PARTITION_NAME) AS last_part FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA zabbix AND TABLE_NAME IN (history,trends) GROUP BY TABLE_NAME;第三条看自动任务执行痕迹。用事件调度器就查information_schema.events的LAST_EXECUTED用 crontab 就看日志文件里有没有新的partition_create或partition_drop记录。-- 事件调度器方案确认事件最近执行时间 SELECT EVENT_NAME, LAST_EXECUTED, STATUS FROM information_schema.EVENTS WHERE EVENT_SCHEMA zabbix;# crontab 方案看最近一次维护日志 tail -n 20 /tmp/CronDBpartitiong.log三条都对上说明分区在正常滚动旧分区按天被 DROP新分区提前建好Zabbix 写入不会因为缺分区报错。之后每周抽空看一眼分区数量和磁盘增长曲线比等 housekeeper 报警再处理从容得多。血泪经验是第一次上线时我只看了SHOW CREATE TABLE有分区就以为完事结果自动任务没生效三天后新数据写不进去前端一片空白。从那以后我每次配完分区都强制走一遍上面三条验证并且第二天再复查一次自动任务的执行记录确认它真的自己跑过一轮而不是靠我手动那一次撑着。希望帮到你。本文还有配套的精品资源点击获取