因为mysql分区要求表达式必须确定,now()等运行时函数不被允许,建表时使用会报error 1486;仅支持to_days(log_time)、year(log_time)等基于列值的纯函数。

为什么不能直接用 NOW() 在分区定义里
因为 MySQL 的 PARTITION BY RANGE 要求分区表达式必须是“确定性函数”,而 NOW()、CURDATE() 这类运行时函数不被允许。建表时若写 PARTITION BY RANGE (TO_DAYS(NOW())),会直接报错 ERROR 1486。真正能用的只有 TO_DAYS(log_time)、YEAR(log_time)、TO_SECONDS(log_time) 这类基于列值的纯函数——它们在插入时才计算,且支持分区裁剪。
add_future_partitions 存储过程的关键参数和陷阱
这个存储过程负责提前创建未来分区,核心在于时间推算与分区名生成是否可预测、无冲突:
-
interval_unit必须是'DAY'、'MONTH'或'WEEK',其他值(如'DAILY')会导致DATE_ADD计算错误 - 分区名建议固定格式,比如
p20261015,避免用DATE_FORMAT(NOW(), 'p%Y%m%d%H%i')——带时分秒的名称会让后续DROP PARTITION难以匹配 - 必须检查目标表是否已存在同名分区,否则
ALTER TABLE ... ADD PARTITION会报ERROR 1517;可在执行前查INFORMATION_SCHEMA.PARTITIONS - 如果用
TO_DAYS()分区,VALUES LESS THAN值必须是整数,不能传字符串'2026-10-15'直接拼接,得先TO_DAYS('2026-10-15')
删除过期分区前必须确认保留策略是否生效
自动删分区不是“删最老的”,而是“删早于某时间点的”。常见错误是把保留逻辑写成硬编码天数,却没考虑业务实际归档周期:
- 例如保留 90 天,但执行时刻是
2026-10-13,那应删TO_DAYS('2026-07-13')之前的分区,而不是简单删p202607开头的所有分区——万一某月有多个分区(如按半月切),就会误删 - 更稳妥的做法是:用
SELECT partition_name FROM INFORMATION_SCHEMA.PARTITIONS WHERE table_name = 't' AND table_schema = 'db' AND CAST(REPLACE(partition_name, 'p', '') AS DATE) 动态获取待删分区名 - 注意:MySQL 8.0 不支持在存储过程中直接拼接并执行含变量的
DROP PARTITION pxxx,必须用PREPARE + EXECUTE,且@sql字符串里分区名不能加引号
事件调度器开启后仍不触发的三个真实原因
SET GLOBAL event_scheduler = ON 只是第一步。即使看到 SHOW VARIABLES LIKE 'event_scheduler' 返回 ON,事件也可能静默失败:
- 事件定义里用了
ON COMPLETION PRESERVE,但没写ENABLE,导致状态是SLAVESIDE_DISABLED;查SELECT * FROM mysql.event看status列 - 存储过程里有未声明的变量或未处理的异常,MySQL 默认忽略错误继续执行,但事件日志(
mysql.general_log或错误日志)里会记ERROR 1305 PROCEDURE xxx does not exist - 事件时间用的是服务器本地时区,而你的运维脚本或监控系统按 UTC 解析,造成“以为该跑了却没跑”的错觉;建议统一用
SYSTEM时区,或显式指定AT CURRENT_TIMESTAMP + INTERVAL 1 DAY
分区维护真正难的不是写 SQL,而是让时间推算、分区命名、边界判断三者严丝合缝。哪怕一个 DATE_ADD(..., INTERVAL 1 MONTH) 在月底执行(如 10 月 31 日),也会落到 11 月 31 日——MySQL 自动转成 12 月 1 日,导致下个分区提前一天生成。这种细节不验证,自动化就只是定时扔错误。











