where条件应使用与created_at字段存储时区一致的时间;若created_at存utc时间(推荐),则where条件需用utc时间。

WHERE条件必须用UTC时间还是本地时间?
取决于你的日志表created_at字段存储的是哪种时区的时间。如果应用写入时统一用UTC(推荐),那WHERE created_at 会出错——因为<code>NOW()返回的是数据库服务器本地时间。更安全的做法是用UTC_TIMESTAMP():
DELETE FROM log_table WHERE created_at 如果字段存的是本地时间(比如东八区),且服务器时区设为<code>+08:00</code>,那用<code>NOW()</code>才匹配。
DELETE大表卡住或锁表怎么办?
直接DELETE FROM log_table WHERE created_at 在百万级以上数据上容易长时间持有行锁甚至锁表,导致业务写入阻塞。应该分批删除:<br><pre class="brush:php;toolbar:false;">DELETE FROM log_table WHERE created_at 然后循环执行,每次删完检查影响行数是否为0。关键点:<br></pre>
<ul>
<li>务必在<code>created_at字段上有索引,否则LIMIT无效(MySQL仍需全表扫描)
SLEEP(0.1)(应用层控制),避免IO打满ORDER BY配合LIMIT——MySQL 5.7+对带WHERE + ORDER BY + LIMIT的DELETE不走索引优化误删后能回滚吗?
仅当满足以下全部条件时才能靠事务回滚:
- 使用的是
InnoDB引擎(MyISAM不支持事务) - 删除操作没显式
COMMIT,且连接未断开 - 没开启
autocommit=1(默认开启,所以绝大多数情况删完就不可逆)
CREATE TABLE log_table_bak_202406 AS SELECT * FROM log_table WHERE created_at 或者用<code>mysqldump</code>导出待删数据段,而不是依赖回滚。<h3>有没有比DELETE更省资源的方案?</h3><p>有,但只适用于按时间分区的表。如果日志表已按月/周建了<code>PARTITION</code>,直接<code>DROP PARTITION</code>几乎瞬时完成,不走逐行删除逻辑:<br></p><pre class="brush:php;toolbar:false;">ALTER TABLE log_table DROP PARTITION p202401, p202402, p202403;前提是建表时用了
PARTITION BY RANGE (TO_DAYS(created_at))这类策略。没分区的话,别试图用TRUNCATE——它不能带WHERE,会清空整表。真正麻烦的不是语法,而是删之前没人确认过哪些下游任务还依赖这些旧日志——比如审计系统拉取三个月前的原始记录,一删就报空指针。时区、索引、事务、分区,每个点漏掉一个,清理就会变成事故前奏。










