应使用date_sub(now(), interval 90 day)动态计算边界时间,避免硬编码日期或隐式转换;delete需分批(如limit 10000)并循环执行,配合row_count()终止条件;定时运行须启用event_scheduler并创建周期性event调用该存储过程,且backup_time字段必须为datetime/timestamp类型并建有索引。

存储过程里怎么判断日期是否超过90天?
MySQL 没有内置的“90天前”常量,得靠 DATE_SUB(NOW(), INTERVAL 90 DAY) 计算边界时间。别用 UNIX_TIMESTAMP() 手动算秒数,容易因时区或闰秒出错;也别用 ADDDATE(NOW(), -90),语义不如 DATE_SUB 清晰,且在低版本(如 5.6)中行为不一致。
常见错误是写成 created_at —— 看似一样,但 MySQL 会先尝试把 <code>NOW() 转成数字再减,可能触发隐式转换,导致索引失效或结果偏差。
实际清理条件建议统一写成:
WHERE backup_time 前提是 <code>backup_time</code> 是 <code>DATETIME</code> 或 <code>TIMESTAMP</code> 类型,且已建索引。 <h3>DELETE 语句放在存储过程里要注意什么?</h3><p>直接写 <code>DELETE FROM backups WHERE backup_time 很危险:万一表数据量大(比如百万级),单次执行会锁表、阻塞业务、甚至触发 <code>max_execution_time</code> 中断。</code></p><p>应该分批删:</p>
- 加
LIMIT 10000控制每次删的数量 - 用
ROW_COUNT()判断是否还有行被删,循环直到返回 0 - 每次删完加
DO SLEEP(0.1)避免 CPU 扛不住(尤其在从库上)
示例关键片段:
REPEAT DELETE FROM backups WHERE backup_time <h3>怎么让这个存储过程自动定时运行?</h3><p>MySQL 本身不支持 cron 式调度,必须依赖 <code>EVENT</code>。先确认服务启用了事件调度器:<code>SHOW VARIABLES LIKE 'event_scheduler';</code>,如果不是 <code>ON</code>,得执行 <code>SET GLOBAL event_scheduler = ON;</code>(需 SUPER 权限)。</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2108" title="MySQL(Linux)"><img src="https://img.php.cn/upload/manual/001/503/042/69d63e871554d590.png" alt="MySQL(Linux)" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/gongju/2108" title="MySQL(Linux)" class="overflowclass">MySQL(Linux)</a> <p class="overflowclass">MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。</p> </div> <a rel="nofollow" href="/xiazai/gongju/2108" title="MySQL(Linux)" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div><p>创建事件时注意三点:</p>
- 用
ON SCHEDULE EVERY 1 DAY,别用AT—— 那是一次性任务 - 加上
DISABLE ON SLAVE,避免在从库上误删(主从结构下尤其关键) - 事件体里显式调用存储过程,比如
CALL clean_old_backups();,别把 DELETE 逻辑直接塞进事件里
事件创建后,用 SELECT * FROM information_schema.EVENTS WHERE EVENT_NAME = 'ev_clean_backups'; 确认状态是 ENABLED。
备份表结构没设好,存储过程可能白跑
如果 backups 表的 backup_time 字段是 VARCHAR 存的 “2023-01-01 12:00:00”,DATE_SUB 对比会失效——字符串比较走字典序,不是时间逻辑。
必须确保:
- 字段类型是
DATETIME或TIMESTAMP - 有普通 B+Tree 索引:
CREATE INDEX idx_backup_time ON backups(backup_time); - 别依赖主键自增 ID 推算时间,ID 不等于时间顺序,尤其有批量插入或主从延迟时
另外,上线前务必在测试库用 EXPLAIN 检查 DELETE 的执行计划,确认走了 idx_backup_time 索引,而不是全表扫描。
清理逻辑本身不复杂,但日期计算、分批控制、事件启用、索引缺失这四点,任意一个漏掉,都可能让定时任务变成定时事故。










