☰
Zabbix 7.0 LTS 数据库分区实战:告别 housekeeper 告警与历史表膨胀
2026/10/31 18:00:35 网站建设 项目流程

简介:面向Zabbix运维人员与监控系统管理员的一份实操型PDF文档,聚焦Zabbix 7.0 LTS部署后MySQL/MariaDB历史记录与趋势表的分区优化,帮助解决数据量增长带来的数据库性能压力与housekeeper繁忙告警问题。资源包共1个PDF文件,约835KB,内容围绕分区脚本的获取与执行、分区过程的定期自动运行、前端保留天数配置及分区天数调整等环节展开,并附有备份提醒与大型库执行耗时说明,适合具备一定数据库基础、希望提升监控系统维护效率的读者参考。目前已有357人学习。文档以步骤化记录呈现,读者可据此理解分区策略的落地方式,掌握用DROP替代DELETE清理旧数据的思路,并对照自身环境调整历史与趋势保留周期,为长期稳定运行提供可复用的操作依据。

1. 凌晨三点被 housekeeper 告警叫醒:Zabbix 7.0 LTS 数据库分区这件事,越早做越省心

“Zabbix housekeeper processes more than 75% busy”这条告警,大概是监控系统里最反直觉的一种故障——你搭 Zabbix 是为了监控别人,结果先被自己的数据库拖垮了。它通常出现在数据量积累到一定规模之后:历史表和趋势表越堆越大,内务处理进程靠 SQL DELETE 一条条删旧数据,删得越久占用越高,告警阈值一破,前端图表开始卡、新数据写入变慢,严重时采集直接断档。Zabbix 7.0 LTS 作为长期支持版本,很多团队会把它当成未来几年的监控底座,如果数据库层没做分区,这个问题迟早会找上门。这篇记录面向正在被 housekeeper 告警、历史表膨胀、数据库磁盘吃紧折腾的开发者,把分区脚本从下载、建过程、定时执行到前端配置的完整链路拆开讲,中间踩过的坑也一并摆出来。

2. 先搞清楚为什么慢:DELETE 删数据与分区 DROP 的本质差别

2.1 housekeeper 到底在忙什么

Zabbix 从主机采集到的每个值都会写进历史表(history、history_uint、history_str、history_text、history_log),小时级聚合结果写进趋势表(trends、trends_uint)。这些表只增不减,靠 Zabbix 内部的内务处理(Housekeeper)按保留天数定期清理。问题在于,Housekeeper 的清理动作本质是DELETE FROM history WHERE clock < ...,而 InnoDB 执行大范围 DELETE 时要逐行加锁、写 undo log、更新索引,删掉几千万行可能要跑几个小时,期间 CPU 和 IO 都被吃掉,于是就有了那条 75% busy 的告警。

分区方案换了个思路:把 history 表按clock字段做 RANGE 分区,每小时(或每天)一个分区,每个分区就是一个独立的物理表空间。清理旧数据不再走 DELETE,而是ALTER TABLE ... DROP PARTITION,直接丢弃整个分区文件,元数据操作级别,毫秒到秒级完成。这就是“SQL DROP 比 DELETE 更有效”的底层原因,也是分区能把 housekeeper 压力降下来的关键。

2.2 分区前必须确认的三件事

动手之前先确认环境,否则后面每一步都可能翻车。第一,确认数据库类型和版本,MySQL 5.7+/8.0 与 MariaDB 10.x 都支持 RANGE 分区,但事件调度器的默认状态不同;第二,确认 Zabbix 数据库账号有CREATE ROUTINE、ALTER、EVENT权限,脚本里大量使用存储过程和动态 SQL,权限不够会在建过程阶段就报错;第三,确认现有表结构里clock字段是 INT 类型且没有其他分区,已经分过区的表再跑脚本会冲突。

# 登录数据库确认版本与现有分区情况 mysql -u zabbix -p zabbix -e "SELECT VERSION();" mysql -u zabbix -p zabbix -e "SELECT table_name, partition_name FROM information_schema.partitions WHERE table_schema='zabbix' AND table_name IN ('history','trends') AND partition_name IS NOT NULL;"

第一条命令看版本,第二条命令如果返回空结果,说明表还没分区,可以继续;如果返回了分区名,说明之前已经做过分区,需要先评估是沿用旧过程还是重建。这里有个容易忽略的点:Zabbix 7.0 LTS 新建的库默认没有分区,但如果你是从旧版本升级迁移过来的,可能残留了历史分区配置,直接跑脚本会出现“partition already exists”类错误。

2.3 备份不是可选项

原文说新安装可以跳过备份,但只要库里有真实监控数据,这一步就不能省。分区脚本会执行ALTER TABLE,虽然理论上不动数据,但存储过程写错参数、执行中断都可能留下半成品状态。我一般会先做逻辑备份再动手:

# 逻辑备份,只备份结构和数据,压缩存储 mysqldump -u zabbix -p --single-transaction --routines --triggers zabbix | gzip > /backup/zabbix_$(date +%F).sql.gz

--single-transaction保证 InnoDB 表在备份期间不锁表,--routines把存储过程一起导出(分区脚本本身就会创建过程,备份里带上便于回滚),--triggers防止触发器丢失。备份文件落到独立磁盘,别和数据库放同一块盘,否则磁盘满了备份也没了。

3. 建过程阶段就报错:脚本导入失败的四种典型情况

3.1 下载解压后先看脚本里的保留天数

分区脚本zbx_db_partitiong.sql默认配置是历史数据保留 7 天、趋势数据保留 365 天。这个默认值对小型环境够用,但对每天采集量大的环境,7 天历史可能不够排查问题。打开脚本找到partition_maintenance_all过程定义,里面每个CALL partition_maintenance(...)的第三个参数就是保留天数:

-- 脚本中 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

第三个参数是保留天数,第四个参数24表示每个分区覆盖 24 小时(即按天分区),第五个参数3表示提前创建未来 3 个分区。如果采集频率很高、单表日增量大,可以把第四个参数改成1做小时级分区,但分区数量会成倍增加,元数据管理开销也上升,一般中小规模按天分区就够。改完保存再导入,别导入后再改,否则要重建过程。

3.2 导入时报 1064 语法错误

最常见的导入失败是ERROR 1064 (42000),原因通常是客户端版本或字符集问题。脚本里用了DELIMITER $$切换语句分隔符,如果用某些图形化工具直接粘贴执行,工具可能不认DELIMITER,导致过程体被截断。正确做法是用命令行重定向导入:

# 用命令行导入,避免图形工具对 DELIMITER 的兼容问题 mysql -u zabbix -p'你的密码' zabbix < zbx_db_partitiong.sql

注意密码和-p之间不要有空格,否则会被当成库名。如果仍然报 1064,检查脚本文件编码是否为 UTF-8 无 BOM,Windows 下编辑过的文件带 BOM 头会让第一行语句解析失败,用file zbx_db_partitiong.sql看一眼,必要时sed -i '1s/^\xEF\xBB\xBF//'去掉 BOM。

3.3 导入时报 1305 过程不存在

ERROR 1305 (42000): PROCEDURE zabbix.partition_maintenance does not exist这个报错说明过程创建顺序被打乱了。脚本里partition_maintenance_all依赖partition_maintenance,后者又依赖partition_verify、partition_create、partition_drop,必须按脚本原始顺序整体导入,不能只挑其中一段执行。如果之前导入失败留下了残缺过程,先清理再重来:

-- 清理可能残留的半成品过程 DROP PROCEDURE IF EXISTS partition_maintenance_all; DROP PROCEDURE IF EXISTS partition_maintenance; DROP PROCEDURE IF EXISTS partition_verify; DROP PROCEDURE IF EXISTS partition_create; DROP PROCEDURE IF EXISTS partition_drop;

清理后重新完整导入脚本。这里提醒一句,DROP PROCEDURE只删过程定义,不动表数据,可以放心执行。

3.4 大库导入过程本身就很慢

原文提到大型数据库上创建分区过程可能持续数小时,这个“数小时”指的是首次执行partition_maintenance_all时对已有大表做分区转换,而不是导入脚本本身。导入脚本只是创建过程定义,秒级完成;真正耗时的是第一次CALL,因为要把一张没有分区的历史大表重组成多个分区,本质是一次全表重建。所以别在业务高峰期跑首次分区,安排在维护窗口,并且提前确认磁盘剩余空间至少是历史表大小的 1.5 倍——重建期间新旧数据会短暂共存。

4. 定时执行翻车现场:事件调度器与 Crontab 怎么选

4.1 事件调度器没开,过程永远不跑

过程建好了不等于分区会自动维护,必须配置定时触发。原文推荐 MySQL 事件调度器,但很多人漏了最关键的一步:事件调度器默认是关闭的。不开启的话,你创建的事件永远不会执行,几天后分区不再新增,新数据写入时找不到对应分区,Zabbix 日志里就会出现[Z3005] query failed: [1526] Table has no partition for value ...,前端图表变空。

# MySQL/MariaDB 配置文件(如 /etc/mysql/mariadb.conf.d/50-server.cnf) [mysqld] event_scheduler = ON

改完配置必须重启数据库服务才生效,然后验证:

-- 确认事件调度器已开启 SHOW VARIABLES LIKE 'event_scheduler'; -- 期望输出 Value 为 ON

如果配置文件位置不确定,用grep --include=*.cnf -irl / -e "\[mysqld\]"搜索,找到含[mysqld]段的文件再改。注意有些发行版把配置拆成多个.cnf文件,改错文件等于没改,改完一定要用上面的SHOW VARIABLES验证,别只看配置文件。

4.2 创建事件时库名写错

开启调度器后创建事件,把partition_maintenance_all挂上去:

-- 每 12 小时执行一次分区维护,库名必须与实际 Zabbix 库名一致 CREATE EVENT zbx_partitioning ON SCHEDULE EVERY 12 HOUR DO CALL partition_maintenance_all('zabbix');

最后那个'zabbix'是库名参数,如果你的库叫zabbix_prod或别的名字,这里必须跟着改,否则过程内部查information_schema.partitions时匹配不到表,静默什么都不做,看起来事件执行了但分区没变化。验证事件是否真的跑过:

-- 查看事件最近执行时间 SELECT EVENT_NAME, LAST_EXECUTED, STATUS FROM information_schema.events WHERE EVENT_NAME='zbx_partitioning';

LAST_EXECUTED为 NULL 说明从未执行,检查STATUS是否为ENABLED,以及调度器是否真的开着。

4.3 Crontab 方案的两个坑

如果数据库不允许开事件调度器(某些托管环境限制),就用 Crontab 兜底:

# 每天凌晨 3:30 执行分区维护,日志落到独立文件便于排查 30 03 * * * /usr/bin/mysql -u zabbix -p'你的密码' zabbix -e "CALL partition_maintenance_all('zabbix');" >> /var/log/zbx_partition.log 2>&1

第一个坑是密码明文写在 crontab 里,任何能读 crontab 的用户都能看到,建议改用~/.my.cnf存凭据,命令里去掉-p参数;第二个坑是日志路径,原文用/tmp,但/tmp可能被系统清理策略删掉,排查时找不到历史记录,换成/var/log下固定文件更稳妥。另外 Crontab 执行环境的 PATH 和交互式 shell 不同,mysql命令最好写绝对路径/usr/bin/mysql,否则可能报 command not found。

4.4 首次执行后必须验证分区真的建出来了

不管用哪种定时方式,首次手动触发一次并检查结果:

-- 查看 history 表的分区列表 SELECT partition_name, partition_description FROM information_schema.partitions WHERE table_schema='zabbix' AND table_name='history' AND partition_name IS NOT NULL ORDER BY partition_ordinal_position;

正常应该看到多个以p开头、后跟日期时间的分区名,partition_description是 Unix 时间戳边界。如果结果为空,说明过程执行了但没建分区,回头检查库名参数和clock字段类型。如果分区数量只有一两个,说明预创建分区数参数太小,把partition_maintenance的第五个参数调大。

5. 前端配置与分区天数对不上:数据被提前删掉的排查

5.1 Housekeeper 和分区在抢着删数据

分区生效后,Zabbix 前端的管理→一般→Housekeeper 里还有一套保留天数配置。如果这里没关掉“开启内部管家”,会出现两套清理逻辑同时跑:分区按脚本里的天数 DROP 分区,Housekeeper 又按前端配置 DELETE 数据,轻则重复劳动浪费资源,重则前端配置的天数比分区天数短,数据被 Housekeeper 提前删掉,你以为是分区出了问题。

正确配置是:取消勾选“开启内部管家”,勾选“覆盖监控项趋势期间”,然后把历史和趋势的保留天数填成与分区脚本完全一致的值(默认历史 7 天、趋势 365 天)。这里的天数不是给 Housekeeper 用的,而是告诉 Zabbix 前端“数据大概保留这么久”,影响图表时间范围提示,填错会导致前端显示的数据范围与实际不符。

5.2 改了分区天数但事件还在调旧过程

想调整保留天数时,不要重新跑整个脚本,而是新建一个带新天数的过程,再把事件指向新过程。比如历史改 30 天、趋势改 400 天:

DELIMITER $$ CREATE PROCEDURE partition_maintenance_all_30and400(SCHEMA_NAME VARCHAR(32)) BEGIN CALL partition_maintenance(SCHEMA_NAME, 'history', 30, 24, 3); CALL partition_maintenance(SCHEMA_NAME, 'history_uint', 30, 24, 3); CALL partition_maintenance(SCHEMA_NAME, 'history_str', 30, 24, 3); CALL partition_maintenance(SCHEMA_NAME, 'history_text', 30, 24, 3); CALL partition_maintenance(SCHEMA_NAME, 'history_log', 30, 24, 3); CALL partition_maintenance(SCHEMA_NAME, 'trends', 400, 24, 3); CALL partition_maintenance(SCHEMA_NAME, 'trends_uint', 400, 24, 3); END$$ DELIMITER ;

然后更新事件指向新过程:

ALTER EVENT zbx_partitioning ON SCHEDULE EVERY 12 HOUR DO CALL partition_maintenance_all_30and400('zabbix');

用 Crontab 的话,注释掉旧行、新增一行指向新过程,旧过程保留在库里不删,方便回滚。这里的关键认知是:分区天数只影响“未来删多少”,已经 DROP 掉的分区找不回来,所以调小天数前想清楚,调大天数则要等新分区慢慢积累。

5.3 分区后磁盘没降反而涨了

有人反馈分区做完磁盘占用不降反升,原因通常是首次分区重建时新旧表空间短暂共存,加上 InnoDB 的 undo log 和 redo log 膨胀。等首次CALL完成、旧分区按天数陆续 DROP 之后,空间才会释放。如果几天后仍然不降,检查是否有未提交的长事务卡住了 undo 清理,用SHOW ENGINE INNODB STATUS看TRANSACTIONS段。另外innodb_file_per_table必须开启(MySQL 5.7+ 默认开),否则 DROP PARTITION 释放的空间回不到文件系统,只是标记为可复用。

6. 避坑清单:五个真实踩过的坑

现象一:Zabbix 日志刷[1526] Table has no partition for value,前端图表空白。原因是事件调度器没开或事件执行失败,未来分区没提前创建,新数据的时间戳超出了最后一个分区的边界。解决:先SHOW VARIABLES LIKE 'event_scheduler'确认开启,再手动CALL partition_maintenance_all('zabbix')补建分区,然后检查事件LAST_EXECUTED是否正常更新。

现象二:导入脚本报ERROR 1044 Access denied。原因是数据库账号只有增删改查权限,没有CREATE ROUTINE和ALTER。解决:用管理员账号执行GRANT CREATE ROUTINE, ALTER, EVENT ON zabbix.* TO 'zabbix'@'localhost';补权限,FLUSH PRIVILEGES后重试。生产环境别图省事直接用 root 跑,权限最小化原则还是要守。

现象三:分区建出来了,但 housekeeper 告警依旧。原因是前端 Housekeeper 没关,两套清理逻辑并存,DELETE 还在跑。解决:进前端管理→一般→Housekeeper,取消“开启内部管家”,保留天数与分区脚本对齐。改完观察一两个采集周期,告警应逐渐消失。

现象四:Crontab 任务静默失败,日志文件是空的。原因是 crontab 环境变量缺失,mysql命令找不到或密码含特殊字符被 shell 解析。解决:命令写绝对路径,密码改用--defaults-extra-file指定凭据文件,日志重定向用>>追加而不是>覆盖,方便回溯多次执行结果。

现象五:调整保留天数后旧数据没按预期删除。原因是只改了脚本文件但没重建过程,或者事件还指向旧过程。解决:新建带新天数的过程,ALTER EVENT指向它,然后手动CALL一次验证partition_drop的输出里确实列出了被删分区名。分区删除是渐进的,改完当天不会立刻删光,按天观察。

7. 快速定位分区问题的排查路径

遇到分区相关故障,我一般按“先看现象落在哪一层,再逐层往下钻”的顺序走,避免一上来就翻脚本。第一层看 Zabbix 前端和日志:图表空白或断点,先查/var/log/zabbix/zabbix_server.log里有没有[1526]或[Z3005],有就直奔分区边界问题;第二层看数据库事件状态,information_schema.events的LAST_EXECUTED和STATUS能立刻区分“没执行”和“执行了但没效果”;第三层看分区实际状态,information_schema.partitions里分区数量和最大边界时间戳是否覆盖到当前时间之后;第四层才回头看脚本参数和权限。

排查层级检查命令/位置正常表现异常指向
应用层zabbix_server.log 搜 1526/Z3005无相关报错分区边界缺失
调度层SELECT LAST_EXECUTED,STATUS FROM information_schema.events近期有执行、ENABLED调度器未开或事件失效
分区层information_schema.partitions查最大边界边界时间戳大于当前时间预创建分区数不足
配置层前端 Housekeeper 设置内部管家关闭、天数对齐双清理逻辑冲突

还有一个容易被忽略的验证点:分区表的clock字段必须是分区键,如果建表时clock是TIMESTAMP而非INT,RANGE 分区会报类型不匹配。用SHOW CREATE TABLE history\G确认字段类型,Zabbix 标准表结构里clock是int(11),升级迁移过来的库要特别留意。

从那以后我每次给 Zabbix 做数据库优化,都会先把“事件调度器状态、分区最大边界、前端 Housekeeper 开关”这三项做成一个检查脚本,部署完立刻跑一遍,确认三项全绿才离开。分区这件事本身不复杂,复杂的是它横跨数据库配置、存储过程、定时任务和前端设置四层,任何一层漏了都会以“图表空白”这种最不直观的方式暴露出来。希望这份记录能帮你少走几个凌晨三点的弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询