必须用索引字段过滤where条件,不可对时间字段使用函数(如date(created_at)),否则导致索引失效。

WHERE条件必须用索引字段过滤,不能对时间字段加函数
直接写 WHERE DATE(created_at) 看似直观,但会让 <code>created_at 索引失效。MySQL无法在函数调用后的结果上使用B+树索引,全表扫描不可避免。
正确做法是把计算移到右侧,让左侧保持裸字段:
WHERE created_at- 确保
created_at上有普通索引或联合索引前导列 - 如果表有分区(按月/按天),优先考虑按
created_atRANGE 分区,删整分区比走WHERE快得多
DELETE语句要限制单次影响行数,避免锁表和日志爆炸
Session表动辄百万级记录,一次性删几万行可能触发长事务、撑爆binlog、阻塞其他写操作。
分批删除更安全:
- 用
DELETE FROM sessions WHERE created_at 控制每次最多删1000行 - 在应用层或脚本里循环执行,每次删完
SLEEP(0.1)避免CPU打满 - 注意:InnoDB下
LIMIT在非唯一条件中可能跳过某些行,但对过期清理场景可接受
注意事务隔离级别和UNDO日志压力
默认 REPEATABLE READ 下,大范围DELETE会持续占用UNDO页,可能引发 Undo log too large 报错或回滚段争用。
若业务允许短暂不一致,可临时调整:
- 会话级设为
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED - 避免MVCC快照长期持有,减少UNDO生成量
- 生产环境改之前确认无依赖快照读的逻辑(比如某些幂等校验)
别忽略Session表的引擎与统计信息
MyISAM表删大量数据后会锁整个表,且不支持事务;InnoDB虽支持行锁,但若 created_at 索引选择性差(比如大量NULL或相同值),仍可能升级为间隙锁甚至表锁。
检查并优化基础项:
- 运行
ANALYZE TABLE sessions更新统计信息,让优化器选对执行计划 - 确认引擎是
InnoDB(SHOW CREATE TABLE sessions查看) - 如果
created_at允许NULL,考虑加WHERE created_at IS NOT NULL显式排除,避免索引跳过NULL带来的不确定性
EXPLAIN 看一眼 type 是不是 range 或 ref,而不是 ALL。










