简介:这份PDF操作记录面向使用Zabbix 7.0 LTS的运维与监控平台管理人员,聚焦MySQL/MariaDB中历史记录表与趋势表的分区优化,用于缓解数据量增长带来的查询压力及housekeeper进程繁忙告警。内容以实际部署与优化记录为主线,涵盖分区SQL脚本的获取与执行、分区过程的创建、每日自动运行配置、Zabbix前端历史与趋势保留天数调整,以及保留周期修改方法,也涉及脚本传参、数据库连接及事件调度器等配置要点,并提示操作前备份数据库、大型库执行可能耗时较久等注意事项,并说明适用Zabbix 3.0以后版本。资源包共1个PDF文件,大小约835KB,便于在数据库服务器旁查阅或打印对照。目前已有357人学习下载,适合需要落地Zabbix数据库分区、优化数据生命周期管理并降低维护成本的中高级运维人员参考。
1. Housekeeper processes more than 75% busy:这条告警为什么越拖越难收拾
Zabbix 刚部署完的那几天最省心,图表绿油油一片,真正让人半夜爬起来的是数据库服务器上那条Zabbix housekeeper processes more than 75% busy。这条 Zabbix 告警不致命、也不耀眼,却特别耐拖:采集还在跑,前端还能点开,只是 housekeeper 进程常年挂在 90% 以上,MySQL 的 CPU 和 IO 被一条 DELETE 拖着走,前端翻页越来越卡,直到某天 history 表涨到几十 GB,一条 DELETE 跑不完,磁盘先满。很多人第一反应是调大 housekeeper 并发、加内存、换 SSD,全都治标不治本。真正让这套监控系统重新稳定下来的,是把 history 和 trends 从「逐行删」改成「按天整块丢弃」的 MySQL/MariaDB 分区方案。这篇记录从告警出现写到分区过程稳定运行,中间踩过的坑一个不落。
2. 先分清三种慢:housekeeper 卡死之前到底发生了什么
收到 alert,最忌讳的就是直接上手改参数。Zabbix 的「慢」至少有三个来源:采集写入慢、前端查询慢、内务清理慢。三者表现相似,成因完全不同,搞错方向的结果是改了一堆参数、告警照旧,还把本来正常的写入拖下水。这一章先把 housekeeper 的病灶量出来,再决定分区是不是唯一的处理手段。
2.1 采集慢、查询慢、内务慢的三条分界线
采集慢的典型症状是Zabbix server is not running、More than 100 items having missing data for more than 10 minutes、或者队列里Zabbix server的 waiting items 一直降不下来。这类问题查的是 poller 数量、网络可达性、超时设置,跟数据库关系不大。
查询慢的症状是前端打开最新数据或图表时转圈,MySQL 侧能看到大量Sending data状态的查询。这类问题一般查索引、查zbx_系列的触发器表体积、查前端页面上的时间范围是不是拉得太长。
内务慢才是housekeeper processes more than 75% busy的来源。Zabbix 收集的原始值全部写进 history 系列表,小时级聚合写进 trends 系列表。老数据靠 housekeeper 定期删除,而这个「定期删除」在数据库层面是逐行 DELETE。一条 DELETE 删 5000 行,产生 5000 行的 undo、redo 和 binlog;删 500 万行就是 500 万行。表越大,单批删除越慢,一个 housekeeping 周期跑不完,下一个周期又来了,进程占用率就长期高位。
我一般先用一条 SQL 把三者的边界划清楚:
-- 观察当前非空闲会话,把长事务揪出来 SELECT id, user, host, db, time, LEFT(info, 100) AS sql_head, rows_examined FROM information_schema.processlist WHERE command <> 'Sleep' ORDER BY time DESC LIMIT 20;这条语句的重点不是看谁在跑,而是看time和rows_examined两个字段。如果某条DELETE FROM history WHERE clock < ...的time超过 60 秒、rows_examined是几千万,那基本可以确认是 housekeeper 在磨洋工;如果长事务是SELECT ... FROM trends,方向就错了,问题在前端查询或者 trends 索引,跟内务无关。
time的单位是秒,rows_examined是 MySQL 优化器估算的扫描行数,这两个数一起吃才有意义——只看time会被偶尔的锁等待误导。
2.2 用三条 SQL 量出磁盘被哪张表吃光了
确认是内务问题之后,第二步是量体积。Zabbix 的数据库里,history 相关的表通常占全库 80% 以上,但具体是哪几张、增长速率多少,得让数据说话:
-- 表体积排名,先看清楚磁盘被谁占了 SELECT table_name, ROUND(data_length / 1024 / 1024 / 1024, 2) AS data_gb, ROUND(index_length / 1024 / 1024 / 1024, 2) AS index_gb, table_rows FROM information_schema.tables WHERE table_schema = 'zabbix' ORDER BY data_length + index_length DESC LIMIT 10;data_length是聚簇索引(也就是数据本身)的大小,index_length是二级索引的大小,table_rows对 InnoDB 只是估算值,不要拿它跟实际行数较真,判断量级够用就行。这一步的意义在于确认历史表和趋势表是主要矛盾。如果排名第一的是一张业务无关的zbx_auditlog或者zbx_events,那分区历史表解决不了问题,得先查审计日志和触发器配置。
第二条查分区现状:
-- 返回空结果说明这几张表一个分区都没有,housekeeper 在硬删 SELECT table_name, COUNT(*) AS part_cnt FROM information_schema.partitions WHERE table_schema = 'zabbix' AND partition_name IS NOT NULL GROUP BY table_name;如果这条 SQL 一行都不返回,说明数据库完全在走「逐行 DELETE」的老路,这也解释了为什么会一直报警。
第三条查增长速率,估算需要保留的天数:
-- 按天统计历史表写入量,用来反推保留期 SELECT FROM_UNIXTIME(clock, '%Y-%m-%d') AS day, COUNT(*) AS rows_written FROM history_uint WHERE clock >= UNIX_TIMESTAMP(NOW() - INTERVAL 7 DAY) GROUP BY day ORDER BY day;FROM_UNIXTIME的第二个参数是格式化模板,Zabbix 的clock字段存的是 Unix 时间戳,直接用clock分组会得到一堆毫秒级的碎片。这一步的结论通常是:一天几百万到几千万行,按现在的磁盘容量最多扛 10 到 15 天,而前端配置里写的是 90 天。这个差值就是问题根源。
2.3 判断分区是否适合你的三个界限
分区不是无代价的方案,它有三个硬性门槛,越过任一条就得换个思路。
第一,数据量。如果 history 全表加起来不到 2 GB、每天写入不到 10 万行,housekeeper 根本不会报警,加个索引、缩短保留期就完事了,上分区属于给自己找活干。经验值是 history 系列超过 20 GB,或者一天的写入量撑不到期望保留期的一半,分区的收益才开始明显。
第二,表结构。MySQL 的 RANGE 分区要求分区键必须包含在每个唯一索引里。Zabbix 的 history 表只有普通索引history_1 (itemid, clock),没有主键,所以能直接按clock分区;trends 表的主键是(itemid, clock),clock已经在主键里,也能分区。但如果你的版本在升级中给某张表补上了不含clock的主键,ALTER TABLE ... PARTITION BY RANGE (clock)会直接报 1503。
第三,可以接受一次停机窗口。首次把一张几十 GB 的表改成分区表,MySQL 是重建整张表,不是原地改元数据——磁盘临时占用接近翻倍,期间写入会被阻塞。Zabbix 前端会表现为空图,采集端会堆积队列。这件事必须放在维护窗口做,不能在业务高峰硬来。
提示:动手前把
SHOW CREATE TABLE history\G的结果存一份,出问题时这就是唯一能回滚的参考。
3. 建分区过程:zbx_db_partitiong.sql 导入与参数改动
分区这件事在 Zabbix 社区有现成的 SQL 脚本zbx_db_partitiong.sql,它把「建分区、丢分区、校验分区是否存在」封装成了几个存储过程,通过调用过程来完成。脚本是通用的,适用于 3.0 之后的大部分版本,包含 7.0 LTS。但它有几个默认值和新版本特性需要提前核对,否则导入成功也可能白跑。
3.1 五个存储过程各自负责哪些步骤
脚本导入后会在 zabbix 库里创建五个过程,分工非常清晰:
partition_verify:检查目标表有没有分区。如果information_schema.partitions里查不到分区名,说明表还是原始结构,它会执行一次ALTER TABLE ... PARTITION BY RANGE (clock)建出第一个分区。这是整个流程里唯一的重活,几十 GB 的表要跑几十分钟到几小时。partition_create:先查partition_description >= CLOCK是否已有覆盖的分区,没有才执行ALTER TABLE ... ADD PARTITION。这个「先判断再建」的逻辑很关键,重复执行不会报错。partition_drop:用游标遍历information_schema.partitions,把所有名字里的日期部分小于阈值的分区收集起来,拼成一条ALTER TABLE ... DROP PARTITION p1,p2,p3执行。这一步是整篇文章的核心收益点——DROP PARTITION 是元数据操作,删 168 个分区的时间跟删 1 个差不多,而且是秒级。partition_maintenance:调度器。它先调partition_verify保证表已分区,然后在循环里预建未来若干个分区,最后按保留天数调partition_drop清理老分区。partition_maintenance_all:把所有表串起来的入口,内部对 5 张 history 表和 2 张 trends 表各调一次partition_maintenance。
partition_maintenance的四个参数决定了行为,值得逐个记住:
| 参数 | 含义 | 脚本默认值 |
|---|---|---|
KEEP_DATA_DAYS | 保留多少天的数据 | history 7,trends 365 |
HOURLY_INTERVAL | 每个分区覆盖多少小时 | 24(即一天一个分区) |
CREATE_NEXT_INTERVALS | 预先创建多少个未来分区 | 3 |
HOURLY_INTERVAL设成 1 就是小时级分区,7 天会产生 168 个分区外加 3 个预建分区。分区数上去了,DROP的粒度更细,但 InnoDB 每个分区在innodb_file_per_table开启时都是一个独立的.ibd文件,171 个文件加上其他表,可能撞上open_files_limit。日级分区在绝大多数场景下够用,我不建议为了「更精细」把间隔调到小时级。
3.2 导入命令、账号权限与字符集的三个注意点
导入之前先备份。新装的库可以跳过备份,但要清楚跳过的是什么——跳过的是「分区建错之后重建整个库」的退路。
# 备份结构与存储过程,routines 不加会把已有的过程漏掉 mysqldump -u root -p --single-transaction --routines --triggers zabbix \ > /tmp/zabbix_before_partition.sql # 导入分区脚本,创建五个存储过程 mysql -u zabbix -p'ZbxPass' zabbix < /tmp/zbx_db_partitiong.sql # 确认过程都进去了 mysql -u zabbix -p'ZbxPass' zabbix -e "SHOW PROCEDURE STATUS WHERE Db='zabbix';"-p和密码之间不能有空格,写成-p 'ZbxPass'会被当成数据库名,然后报一个跟密码毫无关系的错,这个坑每年都有人踩。--single-transaction让 mysqldump 走一致性快照,不锁表;--routines必须加,否则备份里没有存储过程,回滚时只恢复表结构。
执行SHOW PROCEDURE STATUS后应该看到五行,Db列是zabbix,Type列是PROCEDURE。如果只有两三行,说明脚本导入中途出错了,往上翻一下 mysql 客户端的输出,通常是某一行DELIMITER处理有问题。
权限方面,Zabbix 建库时给的账号一般够用,但创建和执行存储过程需要额外的CREATE ROUTINE和EXECUTE,创建事件还需要EVENT权限。缺权限时的报错是Access denied; you need (at least one of) the SUPER, or SYSTEM_VARIABLES_ADMIN privilege(s),看着像密码错了,其实是权限不够。核对方式:
-- 一次性看清楚 zabbix 账号到底有什么 SHOW GRANTS FOR 'zabbix'@'localhost';输出里应该能看到CREATE ROUTINE、ALTER ROUTINE、EXECUTE、EVENT和TRIGGER。缺哪个补哪个,别图省事直接给ALL PRIVILEGES。
还有一个容易被忽略的点:history_bin。较新的版本里,history 系列不止四张表,二进制类的监控项会写进history_bin。导入脚本之前先SHOW TABLES LIKE 'history%';看一眼,如果有多出来的表,务必在partition_maintenance_all里补上对应的一行调用,否则这张表会一直走 housekeeper 的 DELETE,你会在分区做完之后发现告警只是「降了一点」,没有消失。
3.3 改保留天数:脚本里要动哪几个数字
脚本默认 history 7 天、trends 365 天。如果你的磁盘能扛 30 天历史,改法不是「把 7 换成 30 再重跑一遍脚本」,而是重新定义一个入口过程:
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 ;DELIMITER $$是告诉 mysql 客户端「分号不是语句结束符」,这样过程体里的分号才能保留。忘了写会得到一个语法错误,位置还指向第一行CREATE PROCEDURE,非常反直觉。
三个数字的含义必须对齐:30是保留天数,24是一个分区覆盖 24 小时,3是提前建三个分区。三个数字不匹配会出问题——比如保留 30 天但HOURLY_INTERVAL设成 1,那就是 720 个分区,partition_drop每次要处理的游标结果集大到离谱。
创建完之后不要删旧过程。老的partition_maintenance_all留在库里,切换出问题时改一行调度就能切回去,这是最省事的一次性开关。
3.4 大表首次建分区的耗时估算与观测
partition_verify里的那一次ALTER TABLE是关键路径。它的执行时间大致跟表的物理大小成正比:10 GB 大概十几分钟,50 GB 可能一两个小时,100 GB 以上建议直接考虑「缩短保留期先把表压小,再分区」这条曲线路径。
执行期间可以从另一个会话观测进度:
-- 看 ALTER 卡在哪个阶段,copy to tmp table 表示正在重建 SELECT id, time, state, LEFT(info, 80) AS sql_head FROM information_schema.processlist WHERE info LIKE 'ALTER TABLE%partition%';state是copy to tmp table就说明还在重建数据,rename result table表示接近尾声,Waiting for table metadata lock就意味着有别的会话持有这把表的元数据锁,得先把那个会话找出来。等的过程别急着重启服务——ALTER中断会留下临时表文件,清理起来比从头跑还麻烦。
4. 自动化调度:Event Scheduler 和 Crontab 到底选哪个
过程建好了但没人调用,等于什么都没做。这一章的两条路——MySQL 事件调度器和系统 crontab——功能上等价,区别在于「谁来触发」。选错本身不致命,配错才会导致 Zabbix 停止采集。
4.1 打开 event_scheduler 并确认它真的生效了
MySQL 的事件调度器默认是关闭的,这是一个非常隐蔽的默认值:CREATE EVENT能成功,SHOW EVENTS能看到,但LAST_EXECUTED永远是 NULL。
# 在 [mysqld] 段落下加一行,MariaDB 是 /etc/mysql/mariadb.conf.d/ 下的 .cnf [mysqld] event_scheduler = ON找不到配置文件时用这条定位:
# 全盘找包含 [mysqld] 的配置文件,-l 只列文件名 sudo grep --include=*.cnf -irl -e "\[mysqld\]" /改完必须重启 MySQL,systemctl restart mysql还是mariadb取决于安装包。重启后用一条 SQL 确认:
SHOW VARIABLES LIKE 'event_scheduler';返回ON才算数。返回OFF说明配置文件没被读到(放错目录或者文件名前缀不对),返回DISABLED说明是启动参数里被显式关掉了,得查systemctl cat mysql里的ExecStart。
也可以在运行时临时打开:SET GLOBAL event_scheduler = ON;,但这只在本次进程生命周期内有效,重启就没了。生产环境还是老老实实写配置文件。
4.2 CREATE EVENT 之后怎么确认它真的跑过
CREATE EVENT IF NOT EXISTS zbx_partitioning ON SCHEDULE EVERY 12 HOUR DO CALL partition_maintenance_all('zabbix');EVERY 12 HOUR意味着每 12 小时触发一次,比每天一次更稳——某次触发因为锁冲突失败,12 小时后还有下一次。CALL后面的参数是库名,必须跟实际的 schema 名一致,脚本里写死的是zabbix,改名了要跟着改。
十二小时后查执行情况:
-- LAST_EXECUTED 是唯一可信的执行凭据 SELECT EVENT_NAME, STATUS, INTERVAL_VALUE, INTERVAL_FIELD, LAST_EXECUTED, LAST_ERROR_MESSAGE FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'zabbix'\G\G让结果竖排显示,长字段不会被截断。判断标准只有三条:STATUS是ENABLED;LAST_EXECUTED不是 NULL 且时间在最近 12 小时内;LAST_ERROR_MESSAGE为空。三条里有一条不满足就说明事件没真正跑起来,别急着往下走。
如果LAST_EXECUTED一直是 NULL,多半是event_scheduler没开,或者事件的DEFINER用户被删了。DEFINER是创建事件的那个账号,账号一删事件就失效,报错还藏在LAST_ERROR_MESSAGE里。修复方式是ALTER DEFINER='zabbix'@'localhost' EVENT ...。
4.3 用 Crontab 兜底时日志和密码该怎么处理
如果数据库托管在云上、没有改my.cnf的权限,crontab 是唯一选择。写法本身很简单,坑在密码和日志这两处:
# 编辑 root 的 crontab sudo crontab -e # 每天 03:30 跑一次,输出追加到日志而不是覆盖 30 3 * * * /usr/bin/mysql zabbix -e "CALL partition_maintenance_all('zabbix');" >> /var/log/zbx_partition.log 2>&1注意这里没有-u和-p,密码走配置文件。明文密码写在 crontab 里的问题不只是安全,还包括密码里的特殊字符被 shell 二次解释——$、!、&都会出事,报错看起来像「Access denied」,实际是密码被截断了。
# /root/.my.cnf,权限必须 600,否则 mysql 会拒绝读取 [client] user=zabbix password=ZbxPass host=127.0.0.1chmod 600 /root/.my.cnfmysql客户端默认会读~/.my.cnf的[client]段,但只在权限是 600 或更严时才读——权限宽松会静默忽略,然后回退到匿名连接,报一个Access denied for user ''@'localhost'。
日志用>>追加,别用>。>每次覆盖,出问题回头看日志只剩最后一次的记录,等于没有。看过一次执行日志就知道这个结论长什么样:
+--------------------------------------------------------------+ | msg | +--------------------------------------------------------------+ | partition_create(zabbix,history,p202401150000,1705363200) | | partition_create(zabbix,history,p202401160000,1705449600) | | zabbix.history | p202312150000,p202312160000 | +--------------------------------------------------------------+第一段是新建的分区,p202401150000是分区名,1705363200是VALUES LESS THAN的边界时间戳;第二段是被删掉的分区列表,N/A表示这次没有需要删的。看到N/A不用慌,说明还没到保留天数。
4.4 前端管家里的四个勾和保留期对齐
分区在数据库层面管数据生命周期,Zabbix 前端也必须知道这件事,否则两套清理逻辑会互相干扰。
进「管理」→「一般」→「管家」(Housekeeping),做四件事:
- 取消勾选「启用内部管家」。housekeeper 关掉之后那些 DELETE 就彻底停了,这是告警消失的直接原因。
- 勾选「覆盖监控项趋势期间」。不勾这个,前端会用每个监控项自己的
trends配置,跟分区保留天数对不上。 - 历史数据保留天数填 7(或你改成的 30),趋势数据保留天数填 365(或 400)。这两个数字必须跟脚本里的完全一致。
- 点「更新」保存。
数字必须一致的原因在于:前端天数比脚本小,前端会自己去删,等于白分区;前端天数比脚本大,用户能看到数据库里已经不存在的数据区间,图表上表现为一段莫名其妙的空白。
5. 避坑:五条踩过的坑,每条都能让监控停止采集
分区这件事的失败模式很集中,几乎都发生在「分区边界」和「调度执行」这两个接缝上。下面五条是我在不同环境里真实遇到过的,按现象、原因、解决拆开写。
5.1 现象:图表全空,日志刷 [Z3005] query failed: [1526] Table has no partition for value
原因:写入的数据落在所有已建分区边界之外。最常见的触发是调度器根本没跑——分区建到三天后,第四天开始新数据没有归属分区,InnoDB 直接拒绝插入。第二条路径是服务器时间被 NTP 往回调过,clock时间戳跑到已删分区的区间里。
解决:先看最新分区的上界,再补建。
-- 看 history 表最后几个分区的上界时间,跟当前时间对比 SELECT partition_name, partition_description AS less_than_ts, FROM_UNIXTIME(partition_description) AS less_than_time FROM information_schema.partitions WHERE table_schema = 'zabbix' AND table_name = 'history' ORDER BY partition_ordinal_position DESC LIMIT 5;如果less_than_time已经早于当前时间,直接手动跑一次CALL partition_maintenance_all('zabbix');,它会补齐预建分区。补完立刻去查调度器为什么没跑,别只修表面。
5.2 现象:分区建了,但历史数据还是越来越少,前端查不到 15 天前的图
原因:前端管家里的保留天数没改,还是默认值,housekeeper 虽然关掉了「启用内部管家」,但趋势覆盖那一项没勾,或者历史天数填的是 7 而脚本里是 30。另一种可能是只分了 history,忘了 trends。
解决:两边数字对齐,然后验证。
-- 分区数量和最新边界一起核对,七张表都应该有分区 SELECT table_name, COUNT(*) AS part_cnt, FROM_UNIXTIME(MAX(partition_description)) AS newest_bound FROM information_schema.partitions WHERE table_schema = 'zabbix' AND partition_name IS NOT NULL GROUP BY table_name;part_cnt应该接近「保留天数 + 3」。如果某张表不在结果里,说明它没被分到,回到过程定义里补那一行。
5.3 现象:EVENT 建好了,STATUS 也是 ENABLED,LAST_EXECUTED 一直是 NULL
原因:三种可能。event_scheduler是OFF;事件的DEFINER账号不存在;事件创建时带了DISABLE ON SLAVE而后台其实是只读副本。
解决:按顺序排。
-- 一次查三个维度:调度器状态、事件定义者、事件错误 SHOW VARIABLES LIKE 'event_scheduler'; SELECT EVENT_NAME, DEFINER, STATUS, LAST_ERROR_MESSAGE FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'zabbix';DEFINER那列显示zabbix@localhost,就去mysql.user里确认这个账号还在。LAST_ERROR_MESSAGE如果写着Event execution failed,往上翻 MySQL error log,里面会有真实的 SQL 报错。
5.4 现象:升级 Zabbix 之后第一次 ALTER TABLE 就报 1503
原因:A PRIMARY KEY must include all columns in the table's partitioning function。新版本的表结构里补了主键或者唯一索引,而主键不包含clock,RANGE 分区就建不了。
解决:先用SHOW CREATE TABLE history\G看主键定义。如果确实新增了不含clock的主键,别硬删主键(会破坏 Zabbix 自己的写入逻辑),改成对这张表放弃分区、单独缩短保留期。升级后第一次跑调度前,务必先看一遍目标表的建表语句。
5.5 现象:磁盘没降反升,MySQL 开始报 Too many open files
原因:HOURLY_INTERVAL设成了 1,7 天产生 168 个分区,加上预建和趋势表,总分区数破两百。每次partition_drop之后 InnoDB 不一定立即释放.ibd文件,而分区本身的元数据开销也在。open_files_limit默认值撑不住。
解决:改回日级分区,重建过程,同时调大文件句柄上限。
-- 改回 24 小时一个分区,其余参数不变 CREATE PROCEDURE partition_maintenance_all(SCHEMA_NAME VARCHAR(32)) BEGIN CALL partition_maintenance(SCHEMA_NAME, 'history', 30, 24, 3); -- 其余表按同样方式调用,HOURLY_INTERVAL 统一填 24 END;# my.cnf 里的 [mysqld] 段 [mysqld] open_files_limit = 65535 innodb_file_per_table = ON改完重启。分区数不是越多越好,日级粒度足够让 DROP 变成秒级操作,再细就没有额外收益了。
6. 快速定位:从一条告警走到根因的固定顺序
分区相关的故障排查,我不建议凭直觉猜,每次都按同一个顺序走,四步之内基本能定位。这套顺序的价值在于排除法:每一步都有一个明确的「是/否」结论,走到哪一步停下,问题就在那一层。
第一步,确认 housekeeper 还在不在干活。前端「管理」→「一般」→「管家」里看「启用内部管家」是否勾选。分区做完了还是报警,八成是这里没关掉,housekeeper 还在用 DELETE 删老数据,跟 DROP PARTITION 抢同一批行。
第二步,确认分区存在且边界没过期。
SELECT table_name, COUNT(*) AS part_cnt, FROM_UNIXTIME(MAX(partition_description)) AS newest_bound, NOW() AS now_time FROM information_schema.partitions WHERE table_schema = 'zabbix' AND partition_name IS NOT NULL GROUP BY table_name;三列一起看:part_cnt为 0 说明没分区;newest_bound早于now_time说明预建分区用光了,后面必然报 1526;行数少于 7 行说明有表漏分了。
第三步,确认调度器在执行。
SELECT EVENT_NAME, STATUS, LAST_EXECUTED, LAST_ERROR_MESSAGE FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'zabbix'\GLAST_EXECUTED距今超过 13 小时(按 12 小时调度算),说明调度已经断了。
第四步,手工执行一次验证。
CALL partition_maintenance_all('zabbix');这条能成功执行、返回一串partition_create(...)或者N/A,就说明存储过程和权限都没问题,故障只出在调度环节;执行报错,把报错原文贴出来,比翻十篇文档都快。
把这几步和常见报错整理成一张对照表,出问题时可以直接查:
| 报错/现象 | 最可能的原因 | 第一条排查命令 |
|---|---|---|
[1526] Table has no partition for value | 预建分区用完,调度没跑 | 查MAX(partition_description) |
housekeeper processes more than 75% busy | 内部管家没关,仍在 DELETE | 前端管家页面确认 |
EVENT的LAST_EXECUTED为 NULL | event_scheduler = OFF | SHOW VARIABLES LIKE 'event_scheduler' |
ALTER TABLE报 1503 | 新主键不含分区键 | SHOW CREATE TABLE ...\G |
| 图空白但采集正常 | 前端天数与分区天数不一致 | 对比管家配置与过程参数 |
Access denied ... SUPER | 账号缺EVENT权限 | SHOW GRANTS FOR ... |
还有一个值得养成的习惯:分区上线后的第一周,每天花十秒看一眼执行日志里的边界时间,比事后排查省事太多。血泪经验是,这套东西最危险的时间点不是部署当天,而是部署后的第七天——正好是第一批分区该被删的日子,前端的保留天数如果没对齐,就在这一天暴露。从那以后我每次做完分区,都会在日历上把「第七天」标出来,那天强制走一遍上面四步,一条不跳。希望这套顺序也能帮到你,把这条告警彻底从夜里的手机通知里删掉。
本文还有配套的精品资源,点击获取