大家好,我是你们的老朋友小米,31岁,程序员一枚,最近刚换工作,刚入职某十八线大厂数据库平台组。今天咱们来聊一个我亲身经历、也是MySQL面试中的高频“灵魂拷问”:
“如果你要删除一张表里上百万条甚至上亿条数据,你会怎么操作?”
别看问题就一句话,它背后可不只是“delete from table where xxx”那么简单,一不小心你就能把线上MySQL搞崩、业务挂掉、DBA抓狂、领导发火。
今天我们就换个角度,不光讲怎么答,还聊聊我经历的一个真实场景,把这道“百万级删除”的面试题,讲成一个你忘不掉的故事。
面试现场:“你这句 delete,会炸库知道吗?”
故事发生在不久前——
我在准备跳槽面试时,遇到一个我以为“很基础”的问题。
面试官看了我简历,问:“你以前负责过一些 MySQL 优化吗?”
我:“当然,有几次表数据量在千万级别,做过索引优化、执行计划分析,也做过数据清洗。”
他点点头,又抛出一句:“那如果我让你从一个有几千万数据的表中删除一部分老数据,比如说时间超过一年前的记录,你怎么做?”
我一听这不就很简单嘛,想都没想就回了句:“那就 delete from big_table where created_at < '2024-01-01' 呗。”
他眼皮一抬,轻轻说:“你这句 delete,会炸库知道吗?”
我:“啊……?”
他又问:“你知道 delete 会不会锁表?你知道 undo log、redo log、binlog 会不会爆?你考虑过对业务性能的影响吗?”
我一瞬间大脑宕机了。之前做测试删数据都没考虑过这么多,线上生产环境,我真没敢干。
于是他把我带进了一个更深的世界。
一句 delete 背后的“地雷阵”
我们先来看下,为什么一句 delete 会炸库?
- 大事务问题: 如果你一次 delete 上百万数据,MySQL 会开启一个大事务,把这几百万条都放进 undo log。内存吃不消,磁盘写不下,崩溃不是梦。**
锁表 or 锁行:** 即使有索引,如果你 where 条件没写好,可能会变成全表扫描+行级锁,一锁就是几十分钟,业务全部阻塞,线上业务直接“GG”。**
产生大量 binlog:** delete 本质上是“写操作”,需要写入 redo log、undo log、binlog,三重日志齐飞,磁盘压力瞬间飙升,主从同步也可能延迟、堆积、甚至中断。 - 影响主从同步延迟: binlog 巨大导致主从延迟,不仅报警狂响,甚至影响线上读操作(主写从读架构下,用户查到的可能是旧数据)。
一句“delete”,看着优雅,实则步步杀机。
那我们怎么做才是对的?
面试官问我之后,他看我一脸懵,叹口气说:
“你要慢慢删,分批删,用 limit。还可以用 pt-archiver 工具,或者通过 partition table 加速删除。”
那一刻我像醍醐灌顶,回家后查了一晚资料,总结出了下面这几种常见且安全的做法,今天也借这个机会分享给你。
方法一:limit + sleep 分批删除,慢就是快
这是最常见也最推荐的方案,简单易控。
示例:
你可以用循环脚本(如 shell、Python、Java定时任务等)不断执行,配合 sleep 1s 控制节奏,削峰填谷,安全平滑。
优点:
- 不会产生大事务;
- 每次操作时间短,MySQL能扛得住;
- 可以做断点续传(记录下每次删除的最小id或时间点);
建议:
- 最好根据索引字段做删除,比如按 id、created_at,加快定位;
- 搭配 ORDER BY id,防止不同 batch 内重复;
- 用 autocommit = 1 避免事务积压。
方法二:pt-archiver 工具,大规模删除神器
这是 MySQL 官方推荐的工具之一,由 Percona 提供,能安全、高效地进行大数据量删除或归档。
核心命令:
你甚至可以把老数据导出成文件归档,再删除,减少线上压力。
优点:
- 自动分批、自动限速;
- 支持归档+删除;
- 自带日志、断点续传功能;
适合场景: 数据量大到千万级别以上,又不方便自己写脚本控制的。
方法三:按月分区表,直接 drop 分区(极致性能)
如果你从一开始就知道要定期删除历史数据,那就直接用分区表。
例如按照 created_at 每月一分区:
删除数据?一句话:
直接把整个月的数据“秒删”,而且几乎不占资源!
但注意:
- 分区键要选好,一定要常用作查询和删除的字段;
- 不支持主键跨分区;
- 不支持某些复杂 SQL 操作,比如分区键不能是表达式。
方法四:通过中间表迁移法(适合数据清洗)
如果你不能 delete,但又想去掉一部分数据,还有一种办法:
把你要保留的数据迁移到新表,然后 rename 回来。
当然这需要停机或业务低峰期操作,但比直接 delete 安全。
方法五:在线 TTL(MySQL 8.0 未来方向)
MySQL 8.0+ 开始支持 InnoDB 表空间更细粒度的压缩清理、加上企业版的 HeatWave Engine,未来有希望像 MongoDB 一样支持 TTL 自动过期。
虽然现在不成熟,但可以关注下相关技术趋势,例如:
- MySQL Event + 定时脚本组合;
- HeatWave + 自动清理机制;
真实经历:曾经删掉业务整整一周的数据……
最后讲个我亲身“事故”——
两年前,我在一家创业公司做数据清洗任务,要删除一年前的日志数据,表大概有800万行。
我不假思索上了一句:
结果啥都没发生,但 CPU 飙到 400%,锁表30分钟,业务全部报警。
最后 DBA 大哥连夜恢复备份,损失了整整一周的用户数据。我被领导约谈,差点卷铺盖走人。
现在回头想想,才知道“删数据”看似简单,其实是需要极致细节与经验的活儿。
面试怎么答才专业?
我们再回到最初的问题:“你怎么从一张表中安全地删除百万条数据?”
标准答案,可以这样回答:
我会优先评估是否可以用分区表,如果是按时间划分的数据,用 ALTER TABLE DROP PARTITION 最快。如果不能分区,我会用 limit + sleep 的方式做分批删除,每次一万条,控制在安全事务大小,避免锁表和binlog膨胀。生产环境建议使用 pt-archiver 工具或者专用脚本,并配合慢日志和监控报警机制确保删除过程稳定、可控。
看到没?这不就既体现了你的架构理解,又展示了你对生产系统的敏感度,还顺带塞了一波工具使用经验,HR都得给你加分!
写在最后
好了,今天就聊到这里。
这篇文章我从自己真实经历出发,一步步讲清楚了删除百万数据不是小事,更是一门关于安全、效率、架构认知的“重技术活”。
如果你看完了,还记得这几个关键词:
limit + sleep,pt-archiver,分区表,锁、binlog、主从延迟
那这篇文章就没白写!
END
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!
咱们评论区见,聊聊你被 delete “坑”过几次吧!