数据分层后的迁移任务应避免与在线查询争用
数据分层后的迁移任务必须与在线查询错峰执行,否则两者会争抢存储和计算资源,直接拖慢线上业务响应,甚至引发超时和雪崩。 这是分层存储架构落地时最容易被忽视的坑,但也是可以靠调度策略完全规避的问题。
数据分层迁移为什么总会跟在线查询“打架”
数据分层的本质是把热数据留在高速盘,把冷数据移到廉价存储,迁移任务就是搬运工,而在线查询是店里的顾客,搬运工在店里来回走,顾客点单就会等更久。
存储分层之后的读写路径变化
传统单层存储下,查询只走一条路,迁移也走同一条路,但数据量小,冲突不明显,分层后,迁移任务要把数据从SSD挪到SATA盘,或者从本地盘挪到对象存储,读写路径变长,涉及网络和不同介质的IO调度,此时如果在线查询恰好也要读同一批正在迁移的数据,就会同时触发两套IO操作。
更麻烦的是,迁移任务通常是批量扫描,顺序读大量数据,而在线查询是随机小IO,顺序读会占满磁盘队列,随机小IO的延迟就成倍上升,行业共识认为,迁移任务对在线查询的影响往往不是来自单个文件,而是来自它长期占用的带宽和队列深度。
争用发生的典型场景
- 凌晨定时迁移撞上定时报表任务,很多团队习惯把迁移放凌晨,但财报、日志分析也在凌晨跑。
- 迁移任务无限制并发,默认参数下,迁移进程会尽可能多吃资源,几台机器同时迁移就能把集群IO打满。
- 冷数据被查询命中,迁移中的冷数据如果被在线用户访问,比如翻历史订单、查旧日志,查询会等迁移释放锁或磁盘,导致超时。
如何制定不干扰在线业务的迁移计划
关键不是“不要迁移”,而是“让迁移学会让路”,需要从时间、速率、优先级三个维度控制。
用限流和优先级把迁移任务“关进笼子”

迁移工具有各自的限流参数,不要用默认值。
- 对于Hadoop DistCp,用
-bandwidth参数限制带宽,比如-bandwidth 50表示每秒最多50MB,根据线上总带宽预留一半给在线查询,具体数值需要压测决定,业内专家指出,限流之后的迁移耗时增加往往在可接受范围内,但查询性能波动会明显收窄。 - 对于Elasticsearch冷热迁移,使用
_cluster/settings动态调慢indices.recovery.max_bytes_per_sec,默认通常是40mb,可以降到20mb。 - 对于MySQL归档迁移,使用
pt-archiver的--limit和--bulk-delete控制批次大小,同时加--sleep参数让每个批次之间休息几秒,给在线事务留出呼吸空间。
除了限流,还要设置进程优先级,Linux下用nice命令启动迁移进程,例如nice -n 10让迁移进程的CPU优先级低于数据库进程,但对磁盘IO,ionice更直接:ionice -c2 -n7表示用best-effort类且最低优先级,这样即使迁移任务突发,在线查询的IO请求也能插队。
错峰窗口怎么选?按业务低谷期动态调整
固定时间窗口并不可靠,因为不同业务的低谷期可能重叠,正确的做法是结合监控数据自动判断。
- 先梳理业务访问曲线,找出查询量低于均值60%的时段,作为候选窗口。
- 再考虑迁移任务的时长,如果迁移需要3小时,但低谷期只有1小时,就拆分成多批,每个小时只迁移一部分。
- 使用调度工具(如Airflow、DolphinScheduler)设定依赖条件:当在线查询的延迟指标超过阈值时,自动暂停迁移任务,延迟恢复后再继续。
实际操作中,可以在迁移脚本里加一个探针脚本,定期检查线上数据库的活跃连接数或慢查询数量。
#!/bin/bash
# 每5分钟检查一次慢查询数,超过100则暂停迁移
while true; do
slow=$(mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries'" | awk 'NR==2{print $2}')
if [ "$slow" -gt 100 ]; then
kill -STOP $MIGRATION_PID
else
kill -CONT $MIGRATION_PID
fi
sleep 300
done

这比盲目依赖cron时间靠谱得多。
迁移任务与在线查询争用时的排查与缓解方法
即便做好了限流和错峰,偶尔也会出现意外,比如某条查询特别慢,或者磁盘故障导致迁移重试,此时需要快速定位是否由迁移引起。
通过监控指标识别争用
不要只看CPU或内存,重点看磁盘等待时间(iowait)、磁盘队列长度(await、svctm)和存储延迟。
- Linux下用
iostat -x 1观察%util和await,如果迁移启动后%util从30%飙升到90%以上,且await超过几十毫秒,基本可以断定争用。 - 对于分布式存储,查看每个数据节点的IOPS和吞吐量,如果迁移节点和查询节点有重叠,会体现在具体磁盘的读写吞吐曲线上。
- 对于数据库,看慢查询日志的时间分布,若慢查询集中在某一小时,且那个时段恰好有迁移任务,就是直接证据。
应急降级:临时暂停迁移,保查询优先
一旦确认争用,立即暂停迁移,不要试图继续限流,因为限流也需要时间生效,而线上业务不能等。
- 暂停HDFS迁移:执行
hadoop distcp -update的任务可以Ctrl+C终止,断点续传靠-update保证下次启动时跳过已复制文件。 - 暂停Elasticsearch恢复:把
indices.recovery.max_bytes_per_sec设置为0,会立即停止正在进行的shard迁移。 - 暂停MySQL归档:杀掉pt-archiver进程,它的事务会回滚,不会留下半截数据。
暂停后,观察查询延迟是否回落,如果回落,说明争用确实由迁移引起,后续把迁移速率再调低一半重新尝试。

如果无法暂停,比如迁移任务已经删除了源数据,那就只能让在线查询走到冷存储介质去读,这种情况下,建议在查询侧加缓存,比如Redis前置缓存热数据,或者给特定的冷数据查询设置更长的超时时间,但这是治标,长期方案还是得把迁移窗口设计得比业务低谷期更保守。
数据分层迁移避免争用的常见问题
问:迁移任务已经限流了,为什么在线查询还是变慢?
限流只限制了迁移端的发送速率,但没限制磁盘队列深度,如果迁移进程使用异步IO,它仍然可能一次性发出大量请求,这些请求在磁盘排队,导致查询的请求排在后面,需要用ionice降低迁移进程的IO优先级,或者将迁移的并发数调低,让磁盘队列始终有空位。
问:冷数据迁移过程中被用户查询,是直接报错还是等待?
取决于存储实现,如果是HDFS,文件正在移动时读请求会得到FileNotFoundException;如果是Elasticsearch,shard迁移中查询会转发到目标节点,耗时增加但不会失败;如果是MySQL归档到历史库,业务层需要能做数据源切换,最稳妥的做法是先对冷数据打标签,让查询路由到快照或副本,避免直接访问正在迁移的数据块。
问:为什么用了错峰窗口,迁移还是影响了业务?
错峰窗口只考虑了时间维度,没考虑资源维度,比如业务低谷期查询量少,但可能有大批批处理任务在跑,或者备份任务正在占用IO,建议在调度时同时检查迁移节点的磁盘IO利用率和网络带宽,这两项指标都低于30%时再启动迁移,而不是只看业务查询量。
数据分层的价值在于让热数据更快,冷数据更省,迁移任务作为后台行为,必须把自己当成“低优先级租户”,限流、错峰、监控、应急暂停,这四步做到位,在线查询和迁移就能和平共处,记住一个原则:迁移任务的进度可以慢,但业务查询的延迟不能等。