必须用 DELETE;TRUNCATE 无法按条件删除、不支持事务回滚且会清空全表,而过期数据需按时间字段(如 created_at)筛选删除。

MySQL 删除过期数据用 DELETE 还是 TRUNCATE?
直接删表或清空表不现实,过期数据通常按时间字段(比如 created_at 或 updated_at)筛选删除,必须用 DELETE。用 TRUNCATE 会丢掉所有数据,且不能带 WHERE 条件,也不支持事务回滚——真删错了没法救。
DELETE FROM logs WHERE created_at 是安全、可控的写法- 如果表没加索引,
created_at字段上没INDEX,执行会全表扫描,可能卡住线上业务 - 大表(千万级以上)直接
DELETE容易锁表、占满 binlog、拖慢主从同步,得拆成小批量删
怎么写安全的分批删除脚本?
单次删太多行会锁表太久,MySQL 默认事务下还可能触发 lock_wait_timeout 超时。正确做法是控制每次删固定行数,用循环+限流。
- 用
DELETE FROM logs WHERE created_at —— 必须带 <code>ORDER BY和LIMIT,否则 MySQL 5.7+ 可能报错 - 删完立刻
SELECT ROW_COUNT()判断是否还有数据可删,为 0 就退出循环 - 每批之间加
SLEEP(0.1)(单位秒),避免 IO 和 CPU 突增,也减轻主从延迟压力 - 别在高峰时段跑,更别在未备份的生产库上直接试
Linux 定时任务里怎么调用 MySQL 命令?
用 crontab 调 mysql 命令行最直接,但密码不能明文写进脚本或命令里,否则 ps aux 能看到,也容易被日志记录。
- 把账号密码存在
~/.my.cnf,权限设为600:[client] host=localhost user=cleaner password=xxx socket=/var/run/mysqld/mysqld.sock
- crontab 写法示例:
0 2 * * * /usr/bin/mysql -S /var/run/mysqld/mysqld.sock your_db -e "DELETE FROM logs WHERE created_at - 建议封装成 shell 脚本,加日志输出和错误检查,比如用
$?判断mysql命令是否成功 - 注意时区:服务器系统时区、MySQL 的
time_zone设置要一致,否则NOW()返回时间可能和预期差 8 小时
为什么删完数据磁盘空间没释放?
这是最常被忽略的一点:InnoDB 表删掉数据后,空间不会自动还给操作系统,只是标记为“可复用”。你查 data_length 会发现没变小,df -h 也看不出变化。
- 想真正回收空间,得执行
OPTIMIZE TABLE logs,但这个操作会锁表、重建表、耗 IO,线上慎用 - 替代方案是建新表 + 重导数据 + 重命名,或者用
ALTER TABLE logs ENGINE=InnoDB(效果类似 OPTIMIZE,但部分版本不锁读) - 更务实的做法是:定期删、定期监控
information_schema.TABLES中的data_free,当它持续超过表大小 20% 再人工介入
真正麻烦的不是写脚本,而是评估删哪几列、删多少、会不会影响关联查询、binlog 日志量有没有爆掉——这些都得看具体表结构和业务逻辑,没法套模板。











