
你有一个上亿行的日志表,查询越来越慢,磁盘空间告急。你要删掉半年前的数据。
直接跑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亿行旧数据。
| 方案 | 耗时 | 主从延迟 | 业务影响 |
|---|---|---|---|
| 直接DELETE | 12小时+ | 延迟3小时 | 全链接锁表,网站间歇性不可用 |
| pt-archiver | 8小时 | 延迟<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优雅执行。