项目背景
对象存储系统系统架构升级后存在新老集群共存情况,为了降低成本,需将老集群数据迁移到新集群,下线并回收老集群资源。
迁移目标
- 迁移数据正确、无遗漏。
- 迁移进度可控。
- 静默迁移。
迁移思考
迁移方案
如下图所示,是一个数据迁移的简单抽象模型
数据从老集群迁移到新集群,如果在迁移中,老集群仍提供写入请求,会有问题。举个例子,同一个对象obj1从老集群迁移到新集群后,外部对obj1发起了写入,这会造成obj1需要重新迁移,由于外部是否对迁移对象有写入操作是无法控制的,所以迁移工作永远无法完成。
如将写入请求全部转到新集群,情况就不一样,如下图所示:
- 所有写入请求由新集群控制,当老集群有写入请求时,需要转发到新集群;
- 新集群接收到写入请求,首先读取本地是否存在该请求数据,若存在,写直接在本地处理,结束;
- 若本地不存在,则需要从远端(老集群)读取数据,并做写入请求处理,最后保存在本地(即新集群),结束;
采用这种方案看起来可以避免老集群写入,老集群的数据都是静止、存量数据,这样能大大降低迁移的技术复杂性。
刚也说到这种方案需要保证老集群有写入,但是在迁移开始前,如何确定没有写流量呢?
解决方案是在开始迁移前增加写流量监控监控老集群写入,这样才能保证迁移时老集群没有写入。对于耗时较长的大对象传输,如果在监控开关打开后60分钟仍未断开的链接会强制断开,因此可能会小概率出现断连现象。
如何保证迁移数据正确、无遗漏呢?
首先说下怎么做到无遗漏,解决办法是迁移进度表,在确定老集群无流量后,按照一定维度,比如桶名 + 租户维度去统计:总对象数,并在迁移中维护好进度表,确保无遗漏。
再说下怎么保证迁移数据正确?
解决办法是对数据做md5、length校验,确保两边数据一致。
迁移数据粒度
对象存储按数据粒度从大到小可分为:集群、租户、桶、对象。一个集群下有N个租户,一个租户下有M个桶,一个桶下有X个对象。在生产环境对象数据大小都是PB级甚至更大,所以如果对象作为数据迁移粒度太细了,且不好控制,而桶一般都是几十万这样一个量级,租户就更小了,所以采用租户+桶粒度来做迁移是非常合适的。
写入冲突处理
写入冲突处理一般解决思路是规避冲突、合并冲突。当出现迁移数据和外部写入冲突时,以外部写入为准,解决方案是对数据增加版本号,出现冲突能保证数据合并到一致性状态,即当出现冲突时保证外部写入的数据永远是高版本的,这样就不会导致高版本数据被覆盖的情况出现。
迁移质量
静默迁移需要谨小慎微,一不小心就容易出现大的线上事故,为此需要重视迁移质量。
把控质量的解决方案是:
- 注重编码规范,制定计划,定期code review。
- 单元测试并保证单元测试覆盖率。
- 测试流水线:alpha测试、beta测试。
迁移流程
基本可分为:断流、迁移中、迁移完成。
- 按租户(或桶)维度打开迁移开关,将迁移开关状态初始化为写入跟踪状态,同时开始维护一张老集群的写入跟踪表。
- 当发现老集群无写入流量,或到达超时时间(半小时)时,将迁移开关设置为待迁移状态,此时所有请求都转发到新集群。若为读请求,则先从本地(新集群)读,本地没有则从远端读(老集群)。同理,写入操作大概可分为对象删除、对象元数据更新、对象覆盖,由于对象删除是逻辑删除,所以处理逻辑和对象元数据更新一样;对象元数据的更新首先也有一个本地读、远端读的过程:本地若存在,说明已经迁移过来了,则直接在本地更新;本地不存在,则需要先从远端读取到,再在本地更新元数据。
- 打开迁移开关,开始迁移,迁移时维护好迁移进度,当出现冲突时,通过版本号合并冲突。
- 当迁移完成后(根据迁移进度判断),将开关设置为迁移完成,此时在该开关维度的请求都在新集群处理(也就是说不会再从老集群读取数据了)。
- 当整个集群迁移完成后,下线开关和适配代码,到此,迁移完成。
- 下线老集群服务、老集群机器和数据在保留一段时间后(比如3个月,半年)回收机器。
关键设计
迁移组件
- 配置中心:存储迁移开关、写入监控数据、迁移进度数据,提供数据读写服务。
- 控制中心:共开发和运维人员使用,可人工控制迁移状态(比如迁移暂停等等)、查看当前迁移进度(提供工具或页面进行查询)。
- 适配代码:从上面的分析老新集群都要做代码适配。
- 迁移job:数据搬运。
迁移状态机
写入监控表
包含以下字段
请求标识
数据ID
请求时间戳
响应时间戳
迁移进度表
包含迁移整体进度、迁移详情
整体进度:
桶名
租户
对象数
已迁移数
迁移位置
是否完成
迁移详情
资源类型
资源ID
比如对象存储资源类型为:桶、对象
总结
本文针对数据迁移这类需求给出了一种在生产环境可行的迁移思路和在迁移思路下迁移的大致流程和关键设计,希望能在实际工作中给予启发,能够是实实在在能够到大家。
The end.
转载请注明来源,否则严禁转载。原文链接