直接delete会锁表,因未用索引导致全表扫描并加表锁,或大事务中未分批、未提交致锁长期持有;需建覆盖索引(如index idx_status_created(status, created_at))、分批删除、确保autocommit=1或及时commit。

能,但必须用对参数、建好索引、避开主键扫描陷阱——否则照样锁表、照样慢。
为什么直接 DELETE 会锁表?
MySQL 的 DELETE FROM t WHERE created_at 如果 <code>created_at 没有索引,InnoDB 会走全表扫描 + 行锁(甚至升级为间隙锁),锁住大量无关行;即使有索引,大事务也会让锁持有时间过长,阻塞写入。pt-archiver 本身不 magic,它只是把“一次删千万”拆成“每次删 100”,但默认仍按主键顺序查删,容易绕过业务字段索引。
关键参数必须配齐:--where + --i + --no-ascend
默认情况下 pt-archiver 强制 ORDER BY PRIMARY KEY 并 FORCE INDEX (PRIMARY),这对按时间归档毫无意义,反而让 WHERE created_at 失去索引优势。必须显式指定业务索引并禁用升序扫描:
-
--i created_at:强制使用created_at索引做范围扫描 -
--no-ascend:跳过ORDER BY id和主键强制索引,让查询真正走created_at索引 -
--limit 1000:每批最多取 1000 行,避免单次查询太重 -
--txn-size 100:每 100 行提交一次事务,控制锁粒度
错误示例:pt-archiver --source ... --where "created_at (没加 <code>--i 和 --no-ascend,照样扫主键)
归档前务必确认索引存在且高效
执行前用 EXPLAIN SELECT * FROM t WHERE created_at 验证是否走了预期索引。常见坑点:
- 复合索引顺序错:比如建了
(status, created_at),但WHERE created_at 单独用,索引失效 - 索引字段类型不匹配:
created_at是DATETIME,但条件里传了字符串'2024-01-01'(隐式转换可能失效) - 统计信息过期:
ANALYZE TABLE t后再跑 pt-archiver,避免优化器选错索引
--purge 模式下仍需 --dest 或 --file 占位?
不需要。只要明确指定 --purge,就表示只删不归档,此时 --dest 和 --file 必须完全不出现,否则报错。但要注意:
-
--purge和--no-delete互斥,不能共存 - 如果想测试效果,先加
--dry-run,它会打印出实际要执行的SELECT和DELETE语句,可手动EXPLAIN验证 -
--why-quit很有用,能告诉你为什么停(比如 WHERE 条件没匹配到数据、达到--run-time时限等)
真正容易被忽略的是:pt-archiver 在 --purge 模式下,删除仍依赖 SELECT 阶段获取的 ID 列表,所以 --i 和 --no-ascend 对删的效率影响是决定性的——不是“删得慢”,而是“找得慢”。











