优先选 DELETE + WHERE + 索引;日志表按时间清理旧数据,无需自增ID连续性与事务保障,TRUNCATE仅适用于全表清空且可停写场景。

用 TRUNCATE 还是 DELETE 清日志表?
直接删数据,TRUNCATE 快、省事、不走事务日志,但会重置 AUTO_INCREMENT,且不能加 WHERE 条件;DELETE 支持条件删除、可回滚,但慢、锁表久、可能撑爆 ibdata1。
日志表通常只按时间清理旧数据,不需要保留自增 ID 连续性,也不需要事务保障——所以优先选 DELETE + WHERE + 索引,而不是 TRUNCATE。除非你真要清空整张表且能停写。
DELETE FROM log_table WHERE create_time 前,确保 <code>create_time有索引,否则全表扫描+锁表几小时很常见- 别在高峰期跑大范围
DELETE,单次删超 10 万行建议分批,比如每次删 5000 行 +SLEEP(0.1) - 用
SELECT COUNT(*)验证删前数量,再用SELECT ROW_COUNT()确认实际影响行数,避免误删
用 PARTITION BY RANGE 实现自动过期
表分区不是“高级功能”,而是日志类表的刚需。按时间字段(如 create_time)做 RANGE 分区后,删旧数据就变成秒级的 DROP PARTITION,不走行级扫描,也不触发 MVCC 清理。
前提是 MySQL 版本 ≥ 5.7(推荐 8.0.23+),且表引擎为 InnoDB,分区键必须是主键/唯一键的一部分(或包含在其中)。
- 建表时就要规划好,比如每月一分区:
PARTITION BY RANGE (YEAR(create_time)*100 + MONTH(create_time)) - 已有表加分区需重建,用
ALTER TABLE ... REORGANIZE PARTITION不行,得CREATE TABLE AS SELECT+ 数据迁移 + 原子替换 - 定期用
ALTER TABLE log_table DROP PARTITION p_202301清旧分区,再用ADD PARTITION补新分区,脚本里记得先SHOW CREATE TABLE校验结构
MySQL 定时任务(EVENT)能直接删数据吗?
能,但不推荐直接在 EVENT 里写大范围 DELETE。MySQL 的 EVENT 是单线程、无重试、失败静默,一旦语句卡住或锁表超时,整个事件就停摆,且不会报错到 DBA 监控里。
更适合的做法是:用 EVENT 调用一个轻量标记(比如写入 cleanup_job 表),再由外部调度器(如 cron + shell 脚本)去查标记、执行真正删除,并记录日志和错误。
- 开启
event_scheduler = ON后,EVENT默认在SYSTEM时区运行,注意和业务时间是否对齐 -
EVENT中禁止调用存储过程以外的复杂逻辑,更不能嵌套调用其他EVENT - 如果非要用
EVENT执行删除,务必加上LIMIT和ROW_COUNT()判断,避免一次干掉全表
expire_logs_days 和日志表是两回事
很多人混淆 MySQL 的二进制日志(binlog)自动清理参数 expire_logs_days 和业务日志表(如 user_action_log)的数据清理——它们完全无关。expire_logs_days 只控制 binlog 文件保留天数,不影响任何业务表。
业务日志表的数据生命周期,必须由你自己设计策略:分区 + 定时 DROP PARTITION,或带索引的 DELETE + 外部调度,没有捷径。
- 检查当前
binlog保留策略:SHOW VARIABLES LIKE 'expire_logs_days',但这和你的log_table一毛钱关系都没有 - 误设
expire_logs_days = 0会导致binlog持续增长,磁盘打满,但绝不会删掉你一张业务日志表 - 备份恢复依赖
binlog,别为了“省空间”盲目调小它,而该清理的是你自己的日志表
EVENT 的静默失败特性——它不报错,也不重试,你以为它在跑,其实早挂了。


















