1. 先说背景:66万商品数据迁移完,磁盘怎么就满了?
1.1 上一次迁移埋下的雷
上一篇文章里,我写过把66万条商品数据从老库热迁移到新集群的全过程,当时主流程跑得还算顺,切换也控制在几分钟内。但有一件事我当时没细说,就是迁移的目标机器是一台库存机器,磁盘是两块1.8T的SATA机械盘做的RAID1,系统盘和数据盘混在一起,分区规划也比较粗糙。说白了,当时只想着先把数据落下来,没仔细算后续增量、日志和索引文件的空间增长。
结果上线跑了两周,问题就来了。
商品数据本身只有不到200G,但ES的索引副本、MySQL的binlog、还有接口层打印的访问日志,全堆在同一块数据盘上。第二周周五晚上,监控告警直接弹出来:数据盘使用率98%。我当时第一反应是"不对,昨天看才83%"。上去一查,原来是商品详情页的静态化文件——每个商品详情都生成了一份HTML缓存,66万商品就是66万个文件,平均每个文件50到80K,加起来快40G,这还只是当天的量,加上历史遗留和清理脚本没跑干净,磁盘直接被塞爆了。
这次我学到的第一个教训是:预估磁盘空间时,永远要把数据体积乘以3到5倍,尤其是带全文索引、日志和静态文件的应用,光算业务数据是最典型的翻车姿势。
1.2 磁盘告警的完整排查链路
磁盘爆满这种事,最忌讳一上来就直接删文件。我当时的排查链路是这样的:
第一步,df -h确认哪个分区满了,顺便看inode是否耗尽。有些情况下df显示还有空间但应用报磁盘满,那就是inode满了,这是两个不同维度的问题。
第二步,用du逐层往下钻,找到占空间的大头目录。命令大概是du -sh /data/* | sort -rh,先看一级目录,再往里钻。这一步能快速定位是哪个应用目录在吃空间。
第三步,用lsof +L1找出"已删除但仍被进程占用"的文件。这种文件很阴,你明明删了,但空间不释放,因为进程还握着文件描述符。我排查的时候发现,Nginx的access.log被logrotate切割了,但主进程还开着旧文件句柄,导致旧日志占的空间一直没释放。
第四步,top和iostat一起看,确认磁盘IO是否也到了瓶颈。当时iostat显示util一直在85%以上,连正常的数据库查询都开始受影响。
排查完之后的结论很清晰:不只是空间不够,磁盘本身的IO能力也已经顶不住了。机械盘做RAID1,随机读写本来就弱,再加上ES索引频繁刷盘和大量小文件写入,整个数据盘的IO队列长期处于排队状态。这就不是简单的清日志能解决的问题了,得从硬件层面做一次升级——给机器加一块NVMe SSD,把热数据挪到SSD上,机械盘只留冷数据和备份。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 换SSD之前,先把方案想清楚
2.1 热迁移的硬约束:为什么不能直接停机拷贝
我给这台机器定的原则是:业务不允许中断,数据不允许丢失。这就决定了整个迁移动作必须是热迁移,不能搞"先把服务停了,慢慢拷数据,拷完再启动"那套——66万商品数据虽然不算天文数字,但全量拷贝加上校验,在机械盘上怎么也要一两个小时,这个窗口业务方肯定不接受。
而且,这批商品数据是持续在变的:用户下单会改库存,运营后台会改价格和上下架状态,ES索引在持续接收更新。你拷贝的时候数据在变,拷完如果不做增量同步和校验,肯定会有数据不一致的问题。
所以方案的核心思路是:利用rsync做"全量同步+增量同步+最终切换"三阶段迁移。第一次rsync把现有数据全量拷到SSD,这个阶段不切流,两边同时跑;之后每隔一段时间做一次增量rsync,把变化的数据补过去;最后找一个业务低峰窗口,再做一次增量同步,然后快速切换挂载点和配置文件,完成割接。整个过程里,对外服务始终没有长时间中断。
这个思路的关键点是:SSD必须能挂载成独立的文件系统,并且要能承载当前数据盘上所有的"热数据目录";机械盘上剩余空间要足以维持增量数据的短暂增长,撑到切换那一刻。
2.2 冷热分层:不是把整个盘搬到SSD就完事
加了一块1T的NVMe SSD之后,我面临一个选择:是把整个数据盘的内容全搬过去,还是只挑热数
