news 2026/11/4 9:40:01

Zabbix 7.0 LTS 数据库分区改造:用 RANGE(clock) 替代 housekeeper 清理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Zabbix 7.0 LTS 数据库分区改造:用 RANGE(clock) 替代 housekeeper 清理

简介:这是一份围绕 Zabbix 7.0 LTS 部署及 MySQL/MariaDB 数据库分区优化的操作记录,面向已有一定监控系统与数据库基础、希望缓解历史记录和趋势表持续膨胀、housekeeper 负载偏高问题的运维人员。压缩包内只有 1 个 PDF 文件,大小约 835KB,便于在服务器或本地直接查阅,内容以分区脚本获取与调用、分区过程创建、定期任务配置、前端保留天数调整为主线,并补充了备份建议、大型数据库执行耗时及常见日志报错等注意事项。内容还结合 zbx_db_partitiong.sql 说明了按小时或按天建立分区、用 DROP 替代 DELETE 清理旧数据的实践路径,可帮助读者理解分区维护流程、定位表无分区等异常,并形成可复用的数据库优化记录。已有 357 人学习,适合作为 Zabbix 日常数据库维护、性能优化与排错思路的参考资料。

1. Zabbix 7.0 LTS 部署与分区改造:先解决 housekeeper 占用超过 75% 导致的警报

一套刚上线两周的 Zabbix 7.0 LTS,采集侧一切正常,第三周开始数据库那台机器磁盘 IO 持续保持在较高水平,前端频繁弹出Zabbix housekeeper processes more than 75% busy,监控图形开始出现断点。去库里查了一下history表,八百多万行,每天还在以几十万条的速度增长。问题不在 agent,也不在 poller,而在于 Zabbix 默认用 housekeeper 执行 DELETE 语句清理过期数据——删一行要走 undo、redo、binlog,还得维护二级索引,量一大就会拖慢整库。

这份操作记录要解决的就是这件事:用RANGE(clock)数据库分区替掉 DELETE,让过期数据从“逐行删除”变成DROP PARTITION的元数据操作,同时把 Zabbix 7.0 LTS 的部署基线、分区脚本、定时调度、前端保留天数这四件事对齐。适合正在运行 Zabbix 7.0、单表已经达到百万甚至千万行、又不想额外引入时序数据库的监控负责人和运维。

2. 部署基线:把 Zabbix 7.0 LTS 装在能承接分区的库上

动手改分区之前,先把基础部署稳定下来。这一步看起来只是走流程,实际上决定了后面分区脚本能不能一次导入成功——字符集、存储引擎、时间函数行为这三样,任何一样不对,分区过程都可能建出来但不会按预期工作。

2.1 数据库选型与系统基线

分区脚本本身对 MySQL 和 MariaDB 都兼容,因为它只用了三样东西:information_schema.partitions、动态 SQL(PREPARE/EXECUTE)和事件调度器。这三样在 MySQL 8.0 和 MariaDB 10.5 以上都是标准能力,不需要额外插件,也不需要NDBCLUSTER。所以选型上不用过度纠结,按现有机房习惯来即可,真正需要卡死的是下面这张表里的几项。

组件建议基线关键说明
操作系统主流 x86_64 Linux时间同步必须开启,分区边界依赖系统时间
数据库MySQL 8.0 系列 / MariaDB 10.5 以上需要支持 InnoDB 原生分区与 Event Scheduler
存储引擎InnoDBMyISAM 不支持事务,Zabbix 7.0 建表默认也是 InnoDB
字符集utf8mb4 / utf8mb4_bin排序规则不一致会导致前端安装阶段建表报错
PHP8.2 系列前端向导对版本敏感,版本过低会直接卡在环境检查页
Zabbix7.0 LTSserver、frontend、agent 版本保持一致

数据库字符集这件事值得单独说一句。Zabbix 的库必须用utf8mb4_bin这类二进制排序规则,如果用成utf8mb4_general_ci,前端安装向导在导入server.sql.gz时会在带唯一索引的表上抛出索引长度或排序规则冲突的错误,报错信息还很不直观。建库时就把排序规则写进去,能省掉后面半小时的排查。

# 建库建号,字符集和排序规则一次到位 mysql -uroot -p <<'SQL' CREATE DATABASE zabbix CHARACTER SET utf8mb4 COLLATE utf8mb4_bin; CREATE USER 'zabbix'@'localhost' IDENTIFIED BY 'Zbx_DB_Pass_2024'; GRANT ALL PRIVILEGES ON zabbix.* TO 'zabbix'@'localhost'; FLUSH PRIVILEGES; SQL

这段要说明两点。第一,CREATE DATABASE里的COLLATE utf8mb4_bin是硬性建议,不是可选项,Zabbix 的表结构在导入时依赖它。第二,授权用ALL PRIVILEGES就够了,分区脚本需要CREATE ROUTINE、ALTER ROUTINE、EXECUTE、CREATE EVENT、ALTER、DROP这些权限,一次性授予可以避免后面CALL过程时报 1370 错误。生产环境如果要求最小权限,就按这几个权限逐条授予,不要图省事给SUPER。

2.2 数据库参数与 server 配置对齐

Zabbix 的写入压力集中在 history 系列表,innodb_buffer_pool_size给不够,分区做得再好也会因为刷盘频繁而卡顿。下面这份配置是给一台 16GB 内存、纯数据库角色的机器用的,其他规格按比例缩放即可。

# /etc/mysql/conf.d/10_zbx_tweaks.cnf [mysqld] innodb_buffer_pool_size = 8G innodb_buffer_pool_instances = 4 innodb_file_per_table = 1 innodb_flush_log_at_trx_commit = 2 innodb_log_file_size = 1G innodb_flush_method = O_DIRECT max_connections = 300 event_scheduler = ON

逐项解释一下。innodb_buffer_pool_size是历史表读写命中率的关键,一般给物理内存的 50%~60%,监控库不用留太多给其他进程。innodb_file_per_table = 1必须开,分区表的每个分区都是一个独立表空间文件,关掉这个参数后所有分区挤在 ibdata 里,DROP PARTITION释放空间的效果会大打折扣。innodb_flush_log_at_trx_commit = 2是监控场景的常见取舍,每秒刷一次日志,掉电最多丢一秒数据,对监控指标来说可以接受。最后一行event_scheduler = ON是给第 4 章的自动调度准备的,现在开着不影响什么,后面就不用再重启一次数据库。

对应地,zabbix_server.conf里有几个参数要跟着调。

# /etc/zabbix/zabbix_server.conf 关键片段 DBHost=localhost DBName=zabbix DBUser=zabbix DBPassword=Zbx_DB_Pass_2024 StartPollers=30 StartPollersUnreachable=10 StartTrappers=20 CacheSize=128M HistoryCacheSize=64M ValueCacheSize=128M HousekeepingFrequency=1

CacheSize和HistoryCacheSize按主机数量调,500 台以上主机时 128M 是比较稳的起点。HousekeepingFrequency=1表示 housekeeper 每小时跑一次,这个参数在第 4 章关掉内部管家之后就不再起作用了,但部署阶段先留着,等分区验证通过再动。改完配置记得systemctl restart zabbix-server,然后看日志里有没有database connection相关报错。

2.3 导入初始 schema 与首轮验证

Zabbix 7.0 的建库脚本是压缩包形式,导入时要用zcat走管道,不要先解压再导入,避免中间文件占空间。

# 依次导入结构、图片、初始数据 zcat /usr/share/zabbix-sql-scripts/mysql/server.sql.gz | mysql -uzabbix -p zabbix # 确认表数量,正常在 170 张上下 mysql -uzabbix -p zabbix -e "SELECT COUNT(*) AS tbls FROM information_schema.tables WHERE table_schema='zabbix';"

第一条命令里的路径随发行版打包方式略有差异,常见的还有/usr/share/doc/zabbix-sql-scripts/mysql/,找不到就用rpm -ql或dpkg -L查一下。导入完成后不要急着开前端,先确认history和trends这两张表的存储引擎是 InnoDB:

mysql -uzabbix -p zabbix -e "SHOW TABLE STATUS WHERE Name IN ('history','history_uint','trends','trends_uint')\G" | grep -E "Name|Engine"

如果Engine显示为 InnoDB,说明后续分区没有障碍;如果显示 MyISAM,需要在导入前修正默认存储引擎,否则分区脚本执行到ALTER TABLE ... ADD PARTITION那一步会失败。前端配置阶段按向导填数据库地址、账号、名称即可,安装完成后第一件事是去“报表 → 系统信息”确认 server 正在运行,再观察十分钟,看“最新数据”是否有值进来。这一步验证通过,才有必要继续做分区。

3. 分区原理与脚本拆解:partition_maintenance到底做了什么

导入脚本只需要一条命令,但要改参数、要排查故障,就必须弄清楚脚本里的五个存储过程各自负责什么。这一章先把原理说透,再动手执行,后面出问题时才知道该看哪张表、哪个字段。

3.1 为什么 RANGE(clock) 分区能把 DELETE 变成元数据操作

InnoDB 执行一条DELETE FROM history WHERE clock < ?的代价,远不止“删掉一行”这么简单。它要为每一行写 undo log、写 redo log、写 binlog,还要维护history_1这个二级索引的 B+ 树,删除大量数据后表空间不会立刻回收,页内碎片会让后续查询越来越慢。Zabbix 的 housekeeper 正是这么干的,所以当历史数据积累到千万级,DELETE就会长时间占住 IO,报警随之而来。

分区把一张逻辑表切成若干物理分区,每个分区边界由一个VALUES LESS THAN (clock)的整数时间戳定义。删除过期数据时执行的是ALTER TABLE history DROP PARTITION p202401010000,它只修改数据字典并删除对应的表空间文件,不扫描行、不写 undo、不产生大量 binlog。删除十万行和删除一百万行,耗时几乎一样。

同时,InnoDB 的分区裁剪会在查询阶段生效。Zabbix 前端画图时会带上clock BETWEEN ? AND ?的条件,优化器只扫描落在该时间范围内的分区,历史表变成“按天切片”之后,范围查询扫的分区数从 1 个巨大的表变成几个小分区,IO 局部性明显改善。这也是为什么分区脚本默认按 24 小时切一个分区——贴合监控查询的时间粒度。

提示:分区列必须是主键的一部分,这是 InnoDB 的硬性规则。Zabbix 的history系列表本身没有主键(只有history_1(itemid, clock)这样的普通索引),所以脚本可以直接按clock分区,不需要额外改造表结构。如果你自己加过主键,需要把clock纳入主键,否则会撞上 1503 错误。

3.2 五个存储过程的分工与关键逻辑

脚本导入后会在zabbix库里留下五个过程,它们是层层调用的关系,理解这条调用链,改参数时就不会改错地方。

过程名入参职责
partition_verifyschema、table、hourly_interval检查表是否已分区,没有就补建第一个分区
partition_createschema、table、partition_name、clock检查分区是否已存在,不存在则ADD PARTITION
partition_dropschema、table、delete_below_date用游标找出所有早于指定日期的分区,一次性DROP
partition_maintenanceschema、table、keep_days、hourly_interval、create_next单表维护主流程:校验、建未来分区、删过期分区
partition_maintenance_allschema对 history 五张表和 trends 两张表批量调用上面那个过程

partition_create的核心是一段动态 SQL,它先把“分区是否已存在”的判断做完,再拼出ALTER TABLE语句执行:

-- 摘自 partition_create,去掉注释后的核心逻辑 SELECT COUNT(1) INTO RETROWS FROM information_schema.partitions WHERE table_schema = SCHEMANAME AND table_name = TABLENAME AND partition_description >= CLOCK; IF RETROWS = 0 THEN SET @sql = CONCAT('ALTER TABLE ', SCHEMANAME, '.', TABLENAME, ' ADD PARTITION (PARTITION ', PARTITIONNAME, ' VALUES LESS THAN (', CLOCK, '));'); PREPARE STMT FROM @sql; EXECUTE STMT; DEALLOCATE PREPARE STMT; END IF;

这段的作用是幂等:同一个分区名重复执行不会报错,也不会重复建。partition_description >= CLOCK这个判断很关键,它保证不会在已有更高边界的表上插入一个更低边界的分区——那会直接触发VALUES LESS THAN value must be strictly increasing错误。

partition_drop用的是游标加字符串拼接,先把所有过期分区名收集到@drop_partitions,再用一条ALTER TABLE ... DROP PARTITION p1,p2,p3一次性删掉,比循环逐条 DROP 快得多:

-- 游标筛选条件:去掉分区名首字母 p,转成数字后与保留边界比较 DECLARE myCursor CURSOR FOR SELECT partition_name FROM information_schema.partitions WHERE table_schema = SCHEMANAME AND table_name = TABLENAME AND CAST(SUBSTRING(partition_name FROM 2) AS UNSIGNED) < DELETE_BELOW_PARTITION_DATE;

这里依赖一个约定:分区名必须以p开头,后面跟YYYYMMDDHH00形式的数字。脚本生成分区名用的是FROM_UNIXTIME(..., 'p%Y%m%d%H00'),所以这个约定是自洽的。如果有人手工改过分区名,去掉p前缀后CAST出来的数字就乱了,删错分区的血泪经验大多来自这里。

最上层的partition_maintenance做的事情可以概括成三步:调partition_verify确保表已分区;从“今天零点”开始往后预建CREATE_NEXT_INTERVALS个分区;算出KEEP_DATA_DAYS天前的日期,调partition_drop清掉更早的分区。默认参数 7 / 24 / 3 的含义就是:保留 7 天历史、每 24 小时一个分区、永远预建未来 3 个分区。

3.3 导入脚本并执行第一次分区

把脚本包放到数据库服务器上,解压后先别急着跑,打开zbx_db_partitiong.sql看一眼末尾几行,确认默认天数符合业务要求。

# 解压并查看脚本里的默认保留天数 tar -zxvf zbx_db_partitiong.tar.gz grep -n "partition_maintenance(SCHEMA_NAME" zbx_db_partitiong.sql # 用 zabbix 账号导入,创建五个存储过程 mysql -u zabbix -p'Zbx_DB_Pass_2024' zabbix < zbx_db_partitiong.sql # 确认过程已创建 mysql -u zabbix -p'Zbx_DB_Pass_2024' zabbix -e "SHOW PROCEDURE STATUS WHERE Db='zabbix';"

第一条grep是为了让你看到partition_maintenance_all里那七行调用,历史表用 7 天、趋势表用 365 天是脚本默认值。如果业务要求历史保留 30 天,就在导入前把这几行的数字改掉;如果已经导入了也不要紧,第 6 章会讲怎么不改数据、只换过程来调整天数。

导入成功后执行第一次维护。这是唯一一次会明显耗时的手工操作,因为partition_verify要对尚未分区的表做ALTER TABLE ... PARTITION BY RANGE,本质上是重建整张表。

# 首次执行,大表上可能持续数十分钟到数小时 mysql -u zabbix -p'Zbx_DB_Pass_2024' zabbix -e "CALL partition_maintenance_all('zabbix');" # 检查 history 表的分区情况 mysql -u zabbix -p'Zbx_DB_Pass_2024' zabbix -e "SHOW CREATE TABLE history\G" | grep -A 20 "PARTITION BY"

SHOW CREATE TABLE的输出里应该能看到PARTITION BY RANGE (clock),后面跟着若干个PARTITION p2024xxxx0000 VALUES LESS THAN (17xxxxxxxx)。分区数量 = 已过天数对应的分区 + 未来 3 个预建分区。如果一条分区记录都没有,说明partition_verify那一步没触发,通常是因为表上已经有别的分区定义,或者账号缺少ALTER权限,直接看 MySQL 错误日志里的 1503 / 1142 编号即可定位。

4. 让分区自动跑起来:事件调度器与 Crontab 两条路

过程建好了不代表它会自己运行,第 3 章末尾那次CALL只是一次性的。分区必须每天被执行一次,否则新数据没有分区可写,Zabbix 会直接报[Z3005] query failed: [1526] Table has no partition for value,采集随之中断。这一章给出两种调度方式,选一种即可,不要同时开。

4.1 方案一:用 MySQL 事件调度器托管

事件调度器是数据库自带的能力,好处是不依赖操作系统的 cron 服务,也不会把数据库密码写在 crontab 里。前提是event_scheduler必须是ON,前面 2.2 节的配置文件里已经写了,这里只做验证和创建。

# 确认事件调度器状态,Value 必须是 ON mysql -u zabbix -p'Zbx_DB_Pass_2024' zabbix -e "SHOW VARIABLES LIKE 'event_scheduler';" # 创建每 12 小时执行一次的事件 mysql -u zabbix -p'Zbx_DB_Pass_2024' zabbix -e " CREATE EVENT IF NOT EXISTS zbx_partitioning ON SCHEDULE EVERY 12 HOUR DO CALL partition_maintenance_all('zabbix');"

EVERY 12 HOUR意味着一天跑两次,比每天一次更保险——万一某次因为锁等待失败,12 小时后还有一次机会。partition_maintenance_all('zabbix')里的参数是库名,必须和实际库名一致,脚本内部会拿它去查information_schema,写错就等于什么都没做。

创建后不要只看“事件已存在”就结束,要等至少一个周期,然后查执行记录:

-- 查看事件的最后执行时间与状态 SELECT EVENT_NAME, STATUS, LAST_EXECUTED, INTERVAL_VALUE, INTERVAL_FIELD FROM information_schema.events WHERE EVENT_SCHEMA = 'zabbix'\G

LAST_EXECUTED为 NULL 说明从未触发,常见原因是创建事件的会话里event_scheduler还是 OFF,或者事件被创建成了DISABLED。STATUS字段必须是ENABLED。如果LAST_EXECUTED有值但分区没有增加,问题就在过程内部,回到第 3 章看partition_create的幂等判断。

注意:事件调度器执行的语句不受sql_log_bin之外的会话变量影响,但会受全局time_zone影响。分区名由FROM_UNIXTIME生成,时区一变,分区名里的日期就会偏移,这在第 5 章会展开。

4.2 方案二:用 Crontab 兜底

有些环境不允许开事件调度器,或者 DBA 团队要求所有定时任务统一由系统 cron 管理,这时走 Crontab。写法很简单,但密码处理要讲究,不要直接明文写在命令里。

# 在 zabbix 用户家目录下准备一个只读的选项文件 cat > /var/lib/zabbix/.my.cnf <<'EOF' [client] user=zabbix password=Zbx_DB_Pass_2024 EOF chmod 600 /var/lib/zabbix/.my.cnf # 编辑 root 的 crontab,每天 03:30 执行 # 30 03 * * * /usr/bin/mysql --defaults-extra-file=/var/lib/zabbix/.my.cnf zabbix -e "CALL partition_maintenance_all('zabbix');" >> /var/log/zbx_partition.log 2>&1

--defaults-extra-file让 mysql 客户端从文件读取账号密码,ps里看不到明文,这是最基本的做法。>>追加而不是>覆盖,方便回溯历史执行情况;2>&1保证错误信息也进日志,否则失败时日志里只有一行时间戳,什么都查不到。

日志里正常会看到类似partition_create(zabbix,history,p202401150000,1705...)的行,一行代表建了一个分区;如果出现ERROR 1142,是权限问题;出现ERROR 2013,多半是执行时间过长连接被断开,需要给 mysql 客户端加--connect-timeout并把任务挪到低峰期。

选择哪种方案,判断标准很实在:数据库是独立维护、DBA 允许开事件调度器,就用方案一,它在数据库层闭环,迁移机器时不会漏;如果数据库是共享实例、事件调度器被明确禁用,就用方案二,但要记得把.my.cnf的权限锁死。

4.3 前端 Housekeeping 必须与分区天数对齐

这是整套改造里最容易被跳过、后果又最严重的一步。Zabbix 内部管家默认开启,它会用自己的逻辑删历史数据;分区脚本也在删历史数据。两套机制同时跑,等于一件事做两遍,而且它们的保留天数一旦不一致,就会出现“分区还在、数据已经被 housekeeper 删掉”或者“housekeeper 关了、分区天数配错了,数据永久保留”的局面。

在 Zabbix 7.0 前端按下面顺序操作:

  1. 进入“管理 → 一般 → 管家”。
  2. 取消勾选“历史记录和趋势”下的“开启内部管家”。
  3. 勾选“覆盖监控项趋势期间”。
  4. 在“历史记录”和“趋势”两个输入框里填入天数,历史填 7、趋势填 365,必须与脚本里的KEEP_DATA_DAYS完全一致。
  5. 点击“更新”。

第 2 步关掉内部管家之后,Zabbix 不再执行任何 DELETE,数据的生命周期完全交给分区过程。第 3 步的“覆盖”勾选很关键,不勾的话,每个监控项自己设定的“保留历史”会覆盖全局值,导致实际保留时间和分区天数脱节——表现就是明明配了 7 天,某些 item 的数据却留了 30 天,磁盘占用对不上账。

第 4 步的天数必须和脚本对齐,这一点没有商量余地。分区脚本按 7 天删,前端写 30 天,结果是数据在 7 天后被物理删除,前端却以为还有 30 天,查询返回空,用户以为监控丢数据。反过来,前端写 3 天、脚本按 7 天删,磁盘上会多留 4 天的无用数据,白白占空间。

配置完成后建议立刻做一次对比验证:在前端“最新数据”里挑一个每分钟采集的 item,看它最早能查到哪一天,再和information_schema.partitions里history表最早的分区边界比对,两者相差不应超过 1 天。

5. 避坑与排查:分区上线后最容易出问题的五个点

这套方案本身不复杂,但实际执行时会遇到一批“文档里不写、遇到时很难判断”的问题。下面五条按实际发生频率排序,每条都按“现象 → 原因 → 解决”记录,照着对号入座即可。

5.1 建分区阶段的两个直接故障

现象一:Zabbix 日志刷[Z3005] query failed: [1526] Table has no partition for value 1705xxxxx,前端图形全部变空。

原因:分区边界只覆盖到过去某个时间点,没有为“未来”预留分区。脚本的CREATE_NEXT_INTERVALS参数控制预建数量,默认 3,如果调度任务因为权限或调度器关闭而停了几天,预建分区被消费完,新写入的clock就找不到落点。

解决:先手工补一次CALL partition_maintenance_all('zabbix');把边界推到未来,再检查调度是否真的在跑。如果确认调度正常但依然报错,把CREATE_NEXT_INTERVALS从 3 调到 7,多留一周缓冲,代价只是多几个空分区文件。

现象二:执行CALL partition_maintenance_all('zabbix')报ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function。

原因:history表被加过主键,而主键里不含clock。InnoDB 要求分区列是每个唯一键的子集,加主键的动作可能来自某次“优化”或者第三方的表结构同步工具。

解决:SHOW CREATE TABLE history\G确认主键定义,把clock加入主键,或者直接去掉主键(Zabbix 原生表结构本来就没有主键,去掉更贴近官方设计)。改之前先SELECT COUNT(*)记录行数,改完核对,避免误操作。

5.2 运行期与配置对齐阶段的三个隐患

现象三:分区在增加,但分区名里的日期比实际日期早或晚一天,最早分区被提前删掉。

原因:数据库的time_zone与操作系统时区不一致。分区名由FROM_UNIXTIME(CUR_TIME, 'p%Y%m%d%H00')生成,FROM_UNIXTIME依赖会话时区;而clock字段是纯 Unix 时间戳,与时区无关。两者错开 8 小时,跨零点的分区就会落到错误的日期上。

解决:把数据库全局时区固定为业务时区,例如SET GLOBAL time_zone = '+08:00';并写进配置文件,重启后确认SELECT @@global.time_zone, @@session.time_zone;一致。已经在跑的分区不要手工改名,让脚本按新时区继续建,旧分区等到自然过期被 DROP 掉。

现象四:事件创建成功,LAST_EXECUTED一直是 NULL。

原因:event_scheduler在会话建立时是 OFF,之后虽然改成了 ON,但事件本身是用旧会话创建的,或者事件被创建成了DISABLED。还有一种情况是账号缺少EVENT权限,创建语句静默失败。

解决:SELECT EVENT_NAME, STATUS FROM information_schema.events;先看状态,DISABLED就用ALTER EVENT zbx_partitioning ENABLE;打开。再执行SET GLOBAL event_scheduler = ON;并确认配置文件里[mysqld]段落下确实写了这一行,避免重启后又变回 OFF。

现象五:分区正常删,磁盘却不释放,df一直告警。

原因:innodb_file_per_table被关掉了,所有分区挤在共享表空间里,DROP PARTITION只释放逻辑空间,文件不缩小。也可能是删除操作被长事务阻塞,information_schema.innodb_trx里能看到跑了几个小时的事务。

解决:先SHOW VARIABLES LIKE 'innodb_file_per_table';确认是 ON;如果是 OFF,需要重建表才能生效,代价较大,安排在维护窗口做。然后查长事务:SELECT trx_id, trx_started, trx_mysql_thread_id FROM information_schema.innodb_trx ORDER BY trx_started LIMIT 5;,找到后KILL对应连接,再重新执行一次 DROP。

6. 换保留天数与上线验证:不动历史数据也能改策略

分区跑起来之后,调整保留天数是很常见的需求——磁盘涨得快,想把历史从 30 天压到 7 天;或者业务方要求趋势数据留满两年。这时候不需要重新导入脚本,也不要直接去改已经建好的分区,正确做法是新建一个带新天数的过程,然后把调度指向它。原来的过程留在库里,随时可以切回去。

6.1 新建过程覆盖天数,调度只改指向

假设要把历史改成 30 天、趋势改成 400 天,在数据库里建一个新过程,名字带天数便于区分:

DELIMITER $$ CREATE PROCEDURE partition_maintenance_all_30and400(SCHEMA_NAME VARCHAR(32)) BEGIN -- 历史类表统一保留 30 天,每 24 小时一个分区,预建 3 个 CALL partition_maintenance(SCHEMA_NAME, 'history', 30, 24, 3); CALL partition_maintenance(SCHEMA_NAME, 'history_log', 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_uint', 30, 24, 3); -- 趋势类表保留 400 天 CALL partition_maintenance(SCHEMA_NAME, 'trends', 400, 24, 3); CALL partition_maintenance(SCHEMA_NAME, 'trends_uint', 400, 24, 3); END$$ DELIMITER ;

第一个参数是库名,后面三个数字依次是KEEP_DATA_DAYS、HOURLY_INTERVAL、CREATE_NEXT_INTERVALS。HOURLY_INTERVAL保持 24 表示按天分区,如果某类指标写入量特别大、单分区超过几十 GB,可以改成 12 或 6,切成半天或四小时一个分区,代价是分区数量翻倍,information_schema查询会略慢。

过程建好后,把调度切过去。用事件调度器的:

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

用 Crontab 的,把旧行注释掉、新行加上,保留旧行方便回滚:

# 30 03 * * * mysql --defaults-extra-file=/var/lib/zabbix/.my.cnf zabbix -e "CALL partition_maintenance_all('zabbix');" >> /var/log/zbx_partition.log 2>&1 30 03 * * * /usr/bin/mysql --defaults-extra-file=/var/lib/zabbix/.my.cnf zabbix -e "CALL partition_maintenance_all_30and400('zabbix');" >> /var/log/zbx_partition.log 2>&1

切换后别急着走,前端“管家”里的天数也要同步改成 30 和 400,否则又回到第 4 章说的不一致状态。

6.2 三条验证命令与一个固定巡检习惯

改完之后用三条 SQL 确认结果,分别看分区、看事件、看体积:

-- 1. 各表分区数量与最早/最晚边界 SELECT table_name, COUNT(*) AS part_cnt, FROM_UNIXTIME(MIN(CAST(partition_description AS UNSIGNED))) AS oldest_lt, FROM_UNIXTIME(MAX(CAST(partition_description AS UNSIGNED))) AS newest_lt FROM information_schema.partitions WHERE table_schema = 'zabbix' AND partition_name IS NOT NULL AND partition_description <> 'MAXVALUE' GROUP BY table_name ORDER BY table_name; -- 2. 事件最后一次执行时间 SELECT EVENT_NAME, STATUS, LAST_EXECUTED FROM information_schema.events WHERE EVENT_SCHEMA='zabbix'; -- 3. 各表数据与索引占用 SELECT table_name, ROUND((data_length+index_length)/1024/1024/1024, 2) AS size_gb, table_rows FROM information_schema.tables WHERE table_schema='zabbix' AND table_name LIKE 'history%' OR table_name LIKE 'trends%' ORDER BY size_gb DESC;

第一条里partition_description存的是VALUES LESS THAN的边界值,转成时间后才能和日期对上;加上MAXVALUE过滤是为了兼容手工加过分区的实例。第二条的LAST_EXECUTED距当前时间不应超过 12 小时。第三条用来发现异常增长,正常情况下分区方案跑稳之后,history系列表的总大小应该稳定在一个区间内波动,而不是单调上涨。

我自己的习惯是每周一早上花两分钟把这三条查一遍,重点看两件事:最早分区边界是不是恰好等于“今天减保留天数”,以及history表总大小有没有突破上周的 1.2 倍。有一次就是因为CREATE_NEXT_INTERVALS被误改成 1,周五晚上预建分区耗尽,周末两天的图形全是断点,周一才发现。从那以后我每次改完天数或调度,都会先手工跑一次CALL,再用上面三条 SQL 核对一遍边界,确认无误才让它自己跑。希望这套流程能帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/4 9:38:36

AgentScope 2.0实战:多智能体协作与GraphScope编排深度解析

见过不少AI Agent框架&#xff0c;有些看着热闹&#xff0c;真到落地就露馅——要么只适合Demo&#xff0c;要么把简单的活干得无比复杂。AgentScope是我试过之后愿意在团队里推的一个&#xff1a;多智能体协作的开发体验、部署和监控&#xff0c;都做得比较完整&#xff0c;不…

作者头像 李华
网站建设 2026/11/4 9:38:25

微信开源WeKnora:RAG知识库框架本地部署与检索调优实战

1. 从一条开源公告说起&#xff1a;WeKnora 到底是个什么东西微信团队在开源社区扔出了一个叫 WeKnora 的项目&#xff0c;圈子里讨论度不低。我第一时间把仓库拉下来跑了一遍&#xff0c;又翻了翻 issue 区和几个技术群的讨论&#xff0c;大概摸清了它的定位。简单说&#xff…

作者头像 李华
网站建设 2026/11/4 9:38:23

基于React+FastAPI+DeepSeek的AI简历生成器实战:从Markdown到PDF全链路解析

1. 为什么我要做这个 AI 简历生成器简历这东西&#xff0c;写过的人都懂。每次换工作&#xff0c;最痛苦的不是面试&#xff0c;而是对着 Word 调格式——行距差 0.5 磅、项目符号对不齐、导出 PDF 后字体莫名其妙变了。更别提投不同岗位还得改措辞、调重点&#xff0c;一份简历…

作者头像 李华
网站建设 2026/11/4 9:37:13

LLM增强的生态位进化算法设计与实现

1. 项目概述"Partition to Evolve: Niching-enhanced Evolution with LLMs for Automated Algorithm Discovery"这个标题揭示了一个将进化计算与大语言模型(LLMs)相结合的创新研究框架。作为一名长期跟踪AI算法发展的从业者&#xff0c;我认为这个方向代表了自动化算…

作者头像 李华
网站建设 2026/11/4 9:37:11

SQL Server动态SQL生成JSON分页接口与存储过程封装

简介&#xff1a;一份聚焦 SQL 自动生成 JSON 数据的 docx 技术文档&#xff0c;面向数据库开发、后端接口开发及需要将关系表数据快速转为 JSON 的初中级技术人员&#xff0c;重点解决手工拼接 JSON 字符串繁琐、分页查询与字段动态适配困难等问题。压缩包共 1 个 docx 文件&a…

作者头像 李华
网站建设 2026/11/4 9:37:10

SHAP模型可解释性实战:从Shapley值到可视化避坑

1. 为什么模型越准&#xff0c;团队越不敢用 我见过太多这样的情况&#xff1a;一个模型在验证集上把指标刷到了 0.92&#xff0c;业务方兴冲冲拿去上线&#xff0c;结果风控同事一句"这模型为什么把这位老客户拒了"就把整件事卡住了。指标再漂亮&#xff0c;答不上&…

作者头像 李华