在分布式系统中,我们,经常会,听到,CAP理论,和,BASE理论。
CAP理论,说的是,一个,分布式系统,不可能,同时,满足,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),这三个,需求,最多,只能,满足,其中的,两个。
BASE理论,是,对CAP理论中,AP方案的,一个,延伸。它,包括,三个部分:
- 基本可用(Basically Available):系统,在,出现,不可预知的,故障时,允许,损失,一部分,可用性,保证,核心功能,可用。
- 软状态(Soft state):允许,系统,存在,中间状态,这个,中间状态,不会,影响,系统的,整体可用性。
- 最终一致性(Eventually consistent):系统,不需要,实时,保持,强一致性,只需要,在,一段时间后,最终,达到,一致的,状态。
BASE理论,在,分布式系统的,设计中,应用,非常广泛。但是,很多人,可能,不知道,BASE理论,在,系统迁移中,也,有着,重要的,指导意义。
今天,我就,分享一次,我们,基于BASE理论,进行的,从旧系统到新系统的,迁移实战。整个迁移过程,零停机,零数据丢失,用户无感知。希望,这次,实战经验,能给大家,带来一些,启发。
一、迁移背景
先,介绍一下,迁移的背景。
我们,有一个,核心业务系统,已经,运行了,五年多了。这个系统,是,基于,传统的,单体架构,开发的。数据库,用的是,MySQL,单库单表。
随着,业务的,不断发展,用户量,和,数据量,都,越来越大。这个,旧系统,逐渐,暴露出了,很多问题:
- 性能瓶颈:单库单表,数据量,已经,超过了,几千万条。查询,越来越慢。特别是,一些,复杂的,统计查询,经常,需要,几十秒,甚至,几分钟,才能,返回结果。
- 扩展性差:单体架构,所有的,功能,都,耦合,在,一个,应用里。每次,修改,一个,小功能,都,需要,重新,部署,整个应用。而且,无法,针对,某个,功能模块,进行,独立的,扩容。
- 可用性低:单体架构,一个,地方,出问题,可能,会,导致,整个系统,宕机。而且,数据库,是,单点,一旦,数据库,出问题,整个系统,就,无法使用了。
- 技术栈老旧:旧系统,用的,还是,比较老的,技术栈。很多,新的,技术,和,框架,都,无法,使用。开发效率,越来越低。
基于,以上,这些,问题,我们,决定,对,这个,系统,进行,重构,和,迁移。
新系统,我们,采用了,微服务架构,把,原来的,单体应用,拆分成了,多个,独立的,微服务。数据库,也,进行了,拆分,每个,微服务,有,自己的,数据库。而且,我们,对,一些,大表,进行了,分库分表。
新系统,开发完成后,接下来,就是,最关键的,一步:迁移。把,旧系统的,数据,和,流量,平滑地,迁移到,新系统。
迁移,是,一个,风险很高的,操作。如果,迁移,出了问题,可能,会,导致,数据丢失,数据不一致,系统宕机,用户无法使用,等,严重的,后果。
所以,我们,必须,设计,一套,安全的,可靠的,平滑的,迁移方案。
二、BASE理论,在迁移中的,应用
在,设计,迁移方案,之前,我们,重新,学习了,BASE理论,并且,思考,如何,把,BASE理论,应用到,迁移中。
我们,发现,BASE理论的,三个,核心思想,在,迁移中,都,非常适用:
1. 基本可用(Basically Available)
在,迁移过程中,我们,不追求,系统的,完全可用,100%的,性能。我们,允许,在,迁移过程中,系统的,性能,有,一定的,下降。比如,查询,可能,会,比平时,慢一点。一些,非核心的,功能,可能,会,暂时,不可用。
但是,我们,必须,保证,系统的,核心功能,是,可用的。用户,能够,正常地,使用,核心业务。不能,因为,迁移,导致,核心业务,无法使用。
这,就是,基本可用,在,迁移中的,体现。
2. 软状态(Soft state)
在,迁移过程中,我们,允许,旧系统,和,新系统,之间,存在,短暂的,数据不一致。比如,用户,在,旧系统,下了,一个,订单,这个,订单,可能,需要,过一会儿,才能,同步到,新系统。在,这段时间里,旧系统,和,新系统的,数据,是,不一致的。
但是,这个,不一致,是,暂时的,是,中间状态。它,不会,影响,系统的,整体可用性,也,不会,导致,数据的,永久丢失。
这,就是,软状态,在,迁移中的,体现。
3. 最终一致性(Eventually consistent)
在,迁移过程中,我们,不追求,旧系统,和,新系统的,实时的,强一致性。我们,允许,存在,短暂的,不一致。但是,我们,必须,保证,在,一段时间后,旧系统,和,新系统的,数据,最终,能够,达到,一致。
为了,实现,最终一致性,我们,设计了,数据同步,和,对账,机制。通过,实时同步,和,定时对账,保证,旧系统,和,新系统的,数据,最终,一致。
这,就是,最终一致性,在,迁移中的,体现。
基于,BASE理论的,这些,思想,我们,设计了,一套,平滑的,迁移方案。
三、迁移方案设计
我们的,迁移方案,主要,包括,以下,几个,部分:
1. 双写机制
双写,是,我们,迁移方案的,核心。
在,迁移的,第一阶段,我们,让,旧系统,和,新系统,同时,运行。用户的,写操作(新增、修改、删除),同时,写入,旧系统,和,新系统。
这样,就,保证了,新系统,能够,实时地,获取到,最新的,数据。
双写的,实现,我们,是,在,旧系统的,数据访问层,做了,修改。在,写入,旧数据库的,同时,异步地,写入,新系统。
为什么,是,异步的?因为,如果,是,同步的,那么,新系统的,写入,失败,或者,变慢,就会,影响,旧系统的,正常使用。而异步写入,就,不会,有这个问题。旧系统的,写入,成功了,就,直接,返回,给用户。然后,异步地,把,数据,同步到,新系统。
但是,异步写入,也,有一个,问题,就是,可能,会,丢失数据。比如,旧系统的,写入,成功了,但是,异步写入,新系统,失败了,而且,重试,也,失败了。那么,这条,数据,就,没有,写入,新系统。
为了,解决,这个问题,我们,设计了,消息队列,来,做,异步写入。旧系统,写入,成功后,把,数据,发送到,消息队列。然后,新系统,从,消息队列,消费,数据,写入,新数据库。
消息队列,保证了,数据,不会,丢失。即使,新系统,暂时,不可用,数据,也会,保存在,消息队列里,等,新系统,恢复后,再,消费。
而且,消息队列,还,保证了,数据的,顺序性。同一个,业务实体的,数据,会,按照,顺序,被消费,保证了,数据的,一致性。
2. 历史数据迁移
双写,只能,保证,迁移开始后,新的,数据,能够,同步到,新系统。但是,迁移开始前,旧系统里,已经,存在的,历史数据,还,没有,同步到,新系统。
所以,我们,还,需要,把,旧系统的,历史数据,迁移到,新系统。
历史数据迁移,我们,采用了,离线迁移的,方式。写了,一个,数据迁移工具,从,旧数据库,读取,历史数据,经过,转换,处理后,写入,新数据库。
因为,历史数据,量,比较大,几千万条,所以,我们,采用了,分批迁移的,方式。每次,迁移,一批数据(比如,1万条),迁移完,一批,再,迁移,下一批。
而且,我们,把,历史数据迁移,放在了,凌晨,业务低峰期,进行。这样,就,不会,影响,白天的,正常业务。
历史数据迁移,和,双写,是,同时进行的。也就是说,在,迁移历史数据的,同时,新的,数据,通过,双写,实时地,同步到,新系统。
这里,有一个,需要注意的,问题,就是,历史数据迁移,和,双写,可能,会,冲突。比如,一条,历史数据,正在,被,迁移工具,迁移到,新系统,同时,这条数据,又,被,用户,修改了,通过,双写,写入了,新系统。
为了,解决,这个问题,我们,在,迁移历史数据的时候,做了,幂等性,处理。对于,同一条,数据,不管,迁移多少次,结果,都是,一样的。而且,我们,在,迁移的时候,会,判断,这条数据,在,新系统里,是否,已经,存在,并且,更新时间,是否,比,旧系统的,新。如果,新系统的,数据,更新,那么,就,不覆盖。
这样,就,保证了,历史数据迁移,和,双写,不会,冲突。
3. 灰度切流
当,历史数据,迁移完成,并且,双写,也,运行了,一段时间,数据,基本,一致后,我们,就,开始,灰度切流。
灰度切流,就是,把,用户的,读流量,逐步地,从,旧系统,切换到,新系统。
我们,采用了,按,用户ID,灰度的,方式。比如,先,把,1%的,用户的,读流量,切到,新系统。观察,一段时间,如果,没有问题,再,把,比例,提高到,5%,10%,30%,50%,最后,100%。
灰度切流的,实现,我们,是,在,网关层,做的。网关,根据,用户ID,和,灰度比例,决定,把,请求,路由到,旧系统,还是,新系统。
在,灰度切流的,过程中,我们,密切,监控,新系统的,各项,指标,包括,响应时间,错误率,资源使用率,等。如果,发现,问题,立即,把,流量,切回,旧系统,排查,问题。
而且,在,灰度切流的,过程中,我们,还,保留了,双写。也就是说,用户的,写操作,还是,同时,写入,旧系统,和,新系统。这样,即使,新系统,出了问题,我们,也,可以,随时,把,流量,切回,旧系统,不会,丢失数据。
4. 对账机制
在,整个,迁移过程中,我们,都,运行着,对账机制。
对账,就是,定期地,对比,旧系统,和,新系统的,数据,检查,是否,一致。
我们,设计了,两种,对账:
- 实时对账:对于,每一条,通过,双写,同步的,数据,我们,都会,在,同步完成后,对比,旧系统,和,新系统的,数据,是否,一致。如果,不一致,立即,告警。
- 定时对账:每天,凌晨,我们,会,对,全量的数据,进行,一次,对账。对比,旧系统,和,新系统的,所有数据,检查,是否,一致。如果,发现,不一致,生成,对账差异报告,并且,自动,修复,或者,人工,介入,修复。
对账机制,是,保证,数据,最终一致性的,重要手段。通过,对账,我们,可以,及时地,发现,数据不一致的,问题,并且,及时地,修复。
5. 回滚机制
虽然,我们,做了,很多,保障措施,但是,迁移,还是,有可能,出问题。所以,我们,必须,有,完善的,回滚机制。
回滚,就是,当,新系统,出了,严重的,问题,无法,继续,提供服务时,我们,能够,快速地,把,流量,切回,旧系统,恢复,服务。
因为,我们,在,迁移过程中,一直,保留着,双写,所以,旧系统的,数据,一直,是,最新的。当,需要,回滚时,我们,只需要,在,网关层,把,所有的,流量,都,切回,旧系统,就可以了。
回滚,操作,非常简单,只需要,修改,网关的,配置,几秒钟,就可以,完成。而且,因为,旧系统的,数据,一直,是,最新的,所以,回滚后,不会,丢失数据,也,不会,数据不一致。
完善的,回滚机制,是,我们,敢于,进行,迁移的,底气。有了,回滚机制,即使,出了问题,我们,也,可以,快速地,恢复,不会,造成,太大的,影响。
四、迁移实施过程
方案,设计好后,我们,就,开始,实施,迁移了。整个,迁移过程,分为,以下,几个,阶段:
阶段一:准备阶段(1周)
在,这个阶段,我们,主要,做了,以下,工作:
- 部署,新系统,到,生产环境,但是,不接流量。
- 配置,消息队列,和,双写机制。
- 开发,测试,数据迁移工具。
- 开发,测试,对账工具。
- 配置,监控,告警,日志,等。
- 制定,详细的,迁移计划,和,回滚计划。
- 对,相关人员,进行,培训,和,演练。
阶段二:双写阶段(1周)
在,这个阶段,我们,开启了,双写。用户的,写操作,同时,写入,旧系统,和,新系统。
但是,用户的,读流量,还是,全部,走,旧系统。新系统,只,写入,不,读取。
在,这个阶段,我们,密切,观察,双写的,情况,检查,新系统的,写入,是否,正常,数据,是否,一致。
同时,我们,也,在,这个阶段,进行了,历史数据迁移。每天,凌晨,迁移,一批,历史数据。
阶段三:灰度切流阶段(2周)
当,历史数据,迁移完成,并且,双写,也,运行了,一周,数据,基本,一致后,我们,开始,灰度切流。
我们,按照,1% → 5% → 10% → 30% → 50% → 100%的,节奏,逐步地,把,读流量,切到,新系统。
每个,灰度比例,我们,都,观察,至少,1-2天,确认,没有问题后,再,提高,比例。
在,这个阶段,我们,每天,都,进行,对账,检查,旧系统,和,新系统的,数据,是否,一致。
同时,我们,也,密切,监控,新系统的,各项,指标。如果,发现,问题,立即,降低,灰度比例,或者,回滚。
阶段四:全量切流阶段(1周)
当,灰度比例,达到,100%后,所有的,读流量,都,走,新系统了。但是,我们,还是,保留着,双写。旧系统,还是,在,运行,只是,不接,读流量了。
在,这个阶段,我们,继续,观察,新系统的,运行情况,并且,进行,对账。确认,新系统,稳定,可靠,数据,一致。
阶段五:下线旧系统(1周)
当,新系统,全量切流,运行了,一周,没有,任何问题后,我们,就,准备,下线,旧系统了。
首先,我们,停止了,双写。用户的,写操作,只,写入,新系统。
然后,我们,把,旧系统,的,流量,完全,切掉,旧系统,不再,接,任何流量。
最后,我们,观察了,几天,确认,没有问题后,就,正式,下线了,旧系统,并且,释放了,旧系统的,资源。
至此,整个,迁移,圆满,完成。
五、遇到的问题,和,解决方案
在,迁移过程中,我们,也,遇到了,一些,问题。这里,分享几个,比较典型的,问题,和,解决方案。
问题1:双写数据不一致
在,双写阶段,我们,发现,有,少量的,数据,旧系统,和,新系统,不一致。
经过,排查,我们,发现,原因是,有一些,旧的,代码,没有,走,我们,统一的,数据访问层,而是,直接,操作了,数据库。这些,直接操作数据库的,写操作,没有,触发,双写,所以,没有,同步到,新系统。
解决方案:我们,对,所有的,代码,进行了,排查,把,所有,直接操作数据库的,代码,都,改成了,走,统一的,数据访问层。并且,我们,在,数据库层,加了,触发器,作为,兜底。即使,有,遗漏的,直接操作数据库的,代码,触发器,也,会,把,数据,同步到,新系统。
问题2:历史数据迁移慢
在,历史数据迁移阶段,我们,发现,迁移速度,比,预期的,慢。
经过,排查,我们,发现,原因是,数据迁移工具,每次,只,迁移,一条数据,而且,每条数据,都,需要,做,很多,转换,和,处理,所以,速度,比较慢。
解决方案:我们,对,数据迁移工具,进行了,优化。采用了,批量迁移的,方式,每次,迁移,一批数据(1000条),批量,读取,批量,转换,批量,写入。而且,我们,还,采用了,多线程,并行迁移,提高了,迁移速度。
优化后,迁移速度,提高了,几十倍,很快,就,完成了,历史数据迁移。
问题3:新系统性能不达标
在,灰度切流阶段,我们,发现,新系统的,某些,接口,响应时间,比,旧系统,长,性能,不达标。
经过,排查,我们,发现,原因是,新系统,采用了,微服务架构,一次,请求,可能,会,调用,多个,微服务,网络开销,比较大。而且,一些,复杂的,查询,在,分库分表后,需要,跨库,查询,性能,也,受到了,影响。
解决方案:我们,对,新系统,进行了,性能优化。包括,对,常用的,数据,进行了,缓存;对,复杂的,查询,进行了,优化,或者,做了,冗余;对,微服务之间的,调用,进行了,合并,减少了,网络开销;对,数据库,加了,索引,优化了,SQL。
经过,优化,新系统的,性能,达到了,预期,甚至,比,旧系统,更好。
六、经验总结
这次,迁移,圆满,完成了。整个,迁移过程,零停机,零数据丢失,用户无感知。
回顾,这次,迁移,我们,总结了,以下,几点,经验:
1. BASE理论,是,迁移的,重要指导
BASE理论,不仅,适用于,分布式系统的,设计,也,适用于,系统迁移。基本可用,软状态,最终一致性,这三个,思想,在,迁移中,都,非常适用。
基于,BASE理论,我们,不追求,强一致性,不追求,100%的,可用性,而是,接受,短暂的,不一致,接受,一定的,性能下降,保证,核心功能,可用,保证,数据,最终,一致。这样,就,大大,降低了,迁移的,难度,和,风险。
2. 双写,是,平滑迁移的,关键
双写,是,我们,这次,迁移,能够,平滑进行的,关键。通过,双写,新系统,能够,实时地,获取到,最新的,数据。而且,因为,旧系统,一直,在,运行,数据,一直,是,最新的,所以,我们,随时,可以,回滚,到,旧系统,没有,后顾之忧。
双写,加上,消息队列,保证了,数据,不会,丢失,也,保证了,数据的,顺序性。
3. 灰度切流,是,降低风险的,有效手段
灰度切流,让,我们,能够,逐步地,把,流量,切到,新系统,及时地,发现,问题,并且,在,问题,影响,不大的,时候,就,解决掉。
如果,没有,灰度切流,直接,全量切流,那么,一旦,新系统,有,问题,就,会,影响,所有的,用户,后果,不堪设想。
4. 对账,是,数据一致性的,保障
对账机制,让,我们,能够,及时地,发现,数据不一致的,问题,并且,及时地,修复。
实时对账,保证了,双写的数据,一致。定时对账,保证了,全量的数据,一致。两者,结合,保证了,数据的,最终一致性。
5. 回滚,是,迁移的,底气
完善的,回滚机制,让,我们,敢于,进行,迁移。即使,出了问题,我们,也,可以,快速地,回滚,恢复,服务,不会,造成,太大的,影响。
回滚,机制,越,完善,越,简单,我们,进行,迁移的,底气,就,越足。
6. 充分的,准备,和,测试,是,成功的,前提
这次,迁移,能够,成功,和,我们,前期,充分的,准备,和,测试,是,分不开的。
我们,在,迁移前,做了,大量的,准备工作。开发,测试,了,数据迁移工具,对账工具,等。制定了,详细的,迁移计划,和,回滚计划。并且,进行了,多次,演练。
充分的,准备,和,测试,让,我们,在,迁移过程中,遇到,问题时,能够,从容,应对,快速,解决。
七、写在最后
以上,就是,我们,基于BASE理论,进行的,从旧系统到新系统的,迁移实战。
这次,迁移,对,我们,来说,是,一次,非常,宝贵的,经验。它,不仅,让,我们,成功地,完成了,系统迁移,更,让,我们,对,BASE理论,对,分布式系统,对,系统迁移,有了,更深刻的,理解。
BASE理论,不是,什么,高深的,理论,它,是,一种,思想,一种,方法论。它,告诉我们,在,分布式系统中,在,系统迁移中,我们,不需要,追求,完美的,强一致性,不需要,追求,100%的,可用性。我们,可以,接受,一定的,不完美,接受,短暂的,不一致,接受,一定的,性能下降,来,换取,系统的,基本可用,和,数据的,最终一致性。
这种,思想,不仅,适用于,技术,也,适用于,生活。生活中,很多时候,我们,也,不需要,追求,完美。我们,可以,接受,一定的,不完美,接受,短暂的,不如意,来,换取,生活的,基本,幸福,和,最终的,美好。
希望,这篇文章,能给大家,带来一些,启发。如果你,也,有,系统迁移的,经验,或者,对,BASE理论,有,自己的,理解,欢迎在评论区留言,我们一起交流。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录