周四上午十点一刻,客户电话打过来,语气还算平静,说系统"有点卡,页面要转半天,偶尔还报个错"。我心想大清早能有多大事,先远程上去看一眼再说。

top 一开我就愣了:CPU 使用率不到 20%,load average 0.8,内存也够。机器看着闲得很,那卡从哪来?我转手翻了翻他们 MySQL 的慢查询日志,最近十分钟一大片,全是平时几十毫秒的查询,这会儿普遍一两秒起步。数据库没毛病,CPU 没毛病,我心里开始犯嘀咕。

又看了一眼 iostat,答案差点没把我气笑。sda 的 util 直接顶到 99%,await 260 多毫秒。磁盘被打满了。可这家是个进销存系统,上午十点不是业务高峰,谁在没命地写盘?

iotop 一开,凶手就在最上面:一个 rsync 进程,读速度 380MB/s 上下,把磁盘 IO 吃得干干净净。进程从凌晨两点多就起来了,到我看到它那会儿,已经跑了八个多小时。

我当时的第一个念头是:备份不是凌晨就该跑完吗?这台机器的备份方案是我上个月给配的,每天凌晨 02:00 用 rsync 增量同步到一台备份服务器,平时十几分钟就完事。我在 crontab 里让它把输出写到日志,每天早上扫一眼,一直风平浪静。

所以我先怀疑是不是有别人在机器上乱搞。last 查了,没有异常登录。再仔细看那个 rsync 的命令行——源路径是他们的业务数据目录,目标是备份机,参数跟我写的一模一样。得,就是我自己的备份任务。

那问题就变成:它为什么跑了八个多小时还没完。翻了翻备份日志,头天晚上十一点多,客户那边的库房系统做了一次历史数据导入,往业务目录里塞了 200G 出头的文件。增量备份碰上 200G 新数据,等于现场变全量。而我配备份的时候压根没限速,rsync 可着劲跑,从凌晨两点一路顶到上午十点,正好撞上人家上班的点。

这里我得说句公道话:客户导入数据,确实没人通知我。但就算通知了,我的脚本既没限速也没设 IO 优先级,这个洞是实打实在我这。磁盘 IO 这种资源,备份这种低优先级的事,本来就不该跟业务抢。

处理倒不复杂。先 ionice -c3 把那个 rsync 的 IO 优先级降到 idle,让它只捡业务剩下的吃。同步到一半直接 kill 也不合适,断了下次重跑还得重新校验,不如让它慢慢跑完,顺手又给加了个 --bwlimit=50000,也就是限到 50MB/s 左右。剩下的 200G,当天下午也就同步完了,没再出幺蛾子。

收尾做了三件事:一是备份脚本里加上 ionice -c3 和 --bwlimit,从根上让它别跟业务抢;二是把备份开始时间从凌晨两点挪到一点,避开他们的日结批处理;三是跟客户对接人讲清楚,以后往业务目录导大批量数据,提前一天跟我说,我好安排。

周五我看了下备份日志,跑完用了不到四十分钟。客户也没再喊过卡。

这事回头想想挺丢人的。客户导数据把增量顶成准全量,属于不可控因素,怪不到我头上;但备份不限制 IO、跟业务抢了一上午磁盘还没人发现,这属于我没想周全。rsync 这东西,默认就是网速有多快跑多快,磁盘有多快写多快,你不管它,它真能把你整台机器拖死。打那以后,凡是在生产机器上跑的备份任务,我第一件事就是先想清楚:万一它失控了,会不会把业务干掉。