你有一个上亿行的日志表,查询越来越慢,磁盘空间告急。你要删掉半年前的数据。

直接跑DELETE FROM logs WHERE created_at < ‘2026-01-01’?

千万别。

这条语句会锁住大量行,产生几十GB的binlog,主从延迟可能飙到几小时,而且万一你中途想取消,回滚需要同样的时间。

今天介绍一个专业工具:pt-archiver。它能把“一次删千万”拆成“每次删一小批”,在后台慢慢啃,不影响线上业务。

批量删除的正确姿势:先弄懂pt-archiver

pt-archiver是Percona Toolkit中的明星工具,专门设计用于低影响地归档或删除MySQL数据https://manpages.debian.org/testing/percona-toolkit/pt-archiver.1p.en.htmlhttps://docs.percona.com/percona-toolkit/pt-archiver.html?utm_campaign=CodeTengu&utm_medium=web&utm_source=CodeTengu_63。核心思路很简单:每次取一小批数据(比如1000行),处理完再取下一批,每批独立提交事务。

关键参数:

  • --limit:每批取多少行,建议1000
  • --txn-size:多少行提交一次事务
  • --purge:只删除,不归档
  • --sleep:每批之间暂停时间,给服务器喘息机会

实战:清理3个月前过期的订单数据

第一步:安装Percona Toolkit

bash

# Ubuntu/Debian
wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb
sudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb
sudo apt-get update
sudo apt-get install -y percona-toolkit

# CentOS/RHEL
sudo yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm
sudo yum install -y percona-toolkit

验证安装:pt-archiver --versionhttps://blog.csdn.net/weixin_29281941/article/details/158826091。

第二步:确认删除条件有索引

pt-archiver的效率几乎完全取决于索引https://www.php.cn/faq/2456624.html。假设要删orders表中created_at < '2026-02-01'的历史订单:

sql

# 确认索引存在且高效
EXPLAIN SELECT * FROM orders 
WHERE created_at < '2026-02-01' \G

如果type是ALL,说明没走索引,需要先建复合索引:

sql

ALTER TABLE orders ADD INDEX idx_created_at (created_at);

第三步:Dry-run测试

以下命令会用索引扫描数据,但只打印SQL不实际删除https://open.oceanbase.com/blog/8274098544:

bash

pt-archiver --source h=localhost,D=mydb,t=orders,u=root,p=xxx \
  --where "created_at < '2026-02-01'" \
  --purge \
  --limit 1000 \
  --dry-run

观察SQL是否走了正确的索引,rows扫描数是否合理。

第四步:正式执行(带索引优化)

bash

pt-archiver --source h=localhost,D=mydb,t=orders,u=root,p=xxx \
  --where "created_at < '2026-02-01'" \
  --purge \
  --limit 1000 \
  --txn-size 1000 \
  --sleep 1 \
  --statistics

参数解读:

  • --purge:只删除,不归档到其他表
  • --limit 1000:每批取1000行
  • --txn-size 1000:每1000行提交一次事务
  • --sleep 1:每批暂停1秒,避免IO打满
  • --statistics:结束时显示统计信息

pt-archiver的参数陷阱要避开

陷阱一:自增ID最大的行删不掉

这是默认行为。pt-archiver为了防止删除正在写入的最新数据,会自动跳过自增ID最大的那一批https://cloud.tencent.cn/developer/article/2005046?from=15425。

如果要删到最新,加--no-safe-auto-increment参数。

陷阱二:没走预期索引

默认情况下,pt-archiver会按主键顺序扫描https://www.php.cn/faq/2456624.html。如果删除条件不是按主键,效率会极差。

强制指定业务索引:

bash

pt-archiver --source h=localhost,D=mydb,t=logs,i=idx_created_at \
  --where "created_at < '2026-02-01'" \
  --purge --limit 1000 --no-ascend

i=idx_created_at强制使用时间索引,--no-ascend禁用默认的主键升序扫描https://www.php.cn/faq/2456624.html。

陷阱三:没测试直接跑

大表操作前务必在从库或测试环境先跑一遍,用EXPLAIN确认执行计划。

实际效果对比

有个真实案例:一张2亿行的用户行为日志表,需要删除1.5亿行旧数据。

方案耗时主从延迟业务影响
直接DELETE12小时+延迟3小时全链接锁表,网站间歇性不可用
pt-archiver8小时延迟<10秒无感知

虽然pt-archiver总耗时更长,但对线上业务几乎没影响,可以随时中断,中断后还能从断点继续https://manpages.debian.org/testing/percona-toolkit/pt-archiver.1p.en.html。

什么时候不能用pt-archiver?

  • 大批量全删(>80%):用分区表DROP PARTITION或新建表替换的效率更高。
  • 有外键关联:pt-archiver不会主动处理外键,可能导致失败。
  • PXC集群:有已知限制,需谨慎测试。

最后一句

直接DELETE大表的风险远比你想象的大。pt-archiver把重型操作拆解成可控的小批量任务,配合正确的索引,才能实现“数据清理效果”和“业务平稳运行”二者兼得。

下次接到“删历史数据”的需求,别直接敲DELETE了。先用pt-archiver --dry-run看看计划,确认索引,再加--purge优雅执行。