×

通用架构师应该如何把控迁移技术方案【完整版】

96
李艳鹏 595a1b60 08f6 4beb 998f 2bf55e230555
2018.04.29 18:23* 字数 6943

互联网金融行业发生了翻天覆地的变化,相对应的金融科技也在不断的更新和迭代,每次有新的软件系统出炉的时候,就是老的软件系统命运终结的开始,老的项目当然不会束手就擒,它也会做最后的挣扎,当你从它身上迁移用户或者商户的时候,它会给你带来很多麻烦,比如说,它会临时罢工、出现资金损失等等不可忽视的问题,因此,迁移是个大任务,有的时候迁移并不亚于开发一套新系统的难度,甚至可以说是有过之而无不及。

哪些场景需要迁移

我们总结了各种需要迁移的场景。

字段迁移

原来设计的字段大小不能满足现在业务的需求,直接在原表上扩容字段可能会影响线上跑的业务,因此,我们需要增加一个字段来替换原来的字段;字段的数据格式需要升级,通过新增字段来替换原有字段,例如:原有未加密的字段处于安全需求进行加密。

表迁移

用于数据库表设计重构的场景,原有表的结构不合理,新增合理的表来替换原有的表,这时候我们需要表迁移。

数据库迁移

把单库迁移到分库分表的多个库;从一种数据库迁移到另外一种数据库;分库分表的多个库需要扩容的时候,需要进行数据库迁移,迁移到一套能够容纳足够数据的数据库集群。

数据库迁移到其他类型的库

对数据库的选型发生了变化,例如:微博的粉丝库从原有的 MySQL 迁移到 HBase。

系统迁移

原有系统的技术已经过时了,不能满足新需求或者业务发展的需要,开发完成新系统来替换原有系统。

通用的迁移方法论

通用的迁移方法分为平滑迁移和停机迁移,平滑迁移一般采用双写的方案,不需要停机就可以完成迁移,类似飞机的空中加油,或者给运行的汽车换轮子。而停机迁移,则需要停止现有的业务服务,然后迁移数据,待数据迁移之后,再开启对外提供服务。

这里讲解的方法论是一种通用的迁移方案,既适合字段迁移、表迁移,也适合库迁移以及应用迁移,既适合数据库的迁移也适合缓存的迁移,虽然在细节上有些不同,但是在方法论上则大同小异,我们以分片后的数据容量不能满足需求,需要对分片后的数据扩容为例,这里的扩容实际上是迁移的一种特殊案例,我们以扩容为例来说明相应的步骤和实现细节。

平滑迁移

平滑迁移适合对可用性要求较高的场景,例如,线上的交易服务对缓存或者数据库依赖较大,不能忍受停机带来的业务损失,也没有交易的低峰期,我们对此只能采用平滑迁移的方式。

平滑迁移,是指将正在提供线上服务的数据,从一个地方数据存储到另一个数据存储,整个迁移过程中要求不停机,服务不受影响。根据数据所处层次,可以分为缓存迁移、数据库存储迁移、应用迁移等等;根据数据迁移前后的变化,又可以分为数据平移和数据转移。数据库对数据库的迁移属于平移,数据库对其他 NoSQL 库的迁移属于数据转移。

数据平移是指迁移前后数据组织形式不变,比如 MySQL 数据库从1个实例扩展为4个实例,Redis 缓存从2个实例扩展到8个实例等等。

如果在最初的设计里就为以后的扩容做好准备,也就是做了充分的容量评估(关于容量评估,请参考《分布式服务架构:原理、设计与实战》一书中第3章的内容),那么数据迁移工作就会简单很多,比如 MySQL 已经做了分库分表,扩展实例的时候,只需要多做几个从库,切换访问关系,最后将冗余的库表删除即可达到扩容的效果,当然,这需要短暂的停止服务。

近年来出现很多支持自动可伸缩的数据库,在实现上已经做到全自动数据迁移,如 HBase、TiDB 等,那就更简单了,只要通过管理功能来添加机器,手工修改配置或者系统自动发现,就可完成数据库容,也就免去了复杂的数据迁移等工作。

数据转移是指在数据迁移前后,数据组织形式发生了变化。比如将 MySQL 数据库迁移到 HBase 数据库,微博就经历过这样的过程。

平滑迁移通常使用的是双写方案,方案分成4个步骤:双写、迁移历史数据、切读、下双写。

这种方式如果应用于缓存扩容的迁移场景,则还有一个变种,就是不需要迁移旧数据,在第1步中双写后,在一定的时间里通过新规则对新缓存进行写入,新缓存已经有了足够的数据,这样我们就不用再迁移旧数据,直接进入第3步即可。

首先,假设我们的应用现在使用了具有两个分片的数据集群,通过关键字哈希的方式进行路由,如下图所示。

image

因为两个分片已经不能满足容量的需求,所以现在需要扩容到4个分片,达到原来两倍的总大小,因此我们需要迁移。

迁移的具体过程如下。

(1)双写

按照新规则和旧规则同时往新旧数据系统中写数据,如下图所示。

image

这里,我们仍然按照旧的规则,也就是关键字哈希除以2取余来路由分片,同时按照新的规则,也就是关键字哈希除以4取余来路由到新的4个分片上,来完成数据的双写。

这个步骤有优化的空间,因为是在成倍扩容,其实,我们不需要准备4个全新的分片,对于新规则中的前两个分片的数据,其实是旧规则中的两个分片数据的子集,并且规则一致,所以我们可以重用前两个分片,也就是一共需要两个新的分片,用来处理关键字哈希取余后为2和3的情况,使用旧的数据分片来处理关键字哈希取余后0和1的情况即可。如下图所示。

image

(2)迁移历史数据

把旧缓存集群中的历史数据读取出来,按照新的规则写到新的数据集群中,如下图所示。

image

在这个过程中,我们需要迁移历史数据,在迁移的过程中可能需要迁移工具,这也需要一部分开发工作量。在迁移后,我们还需要对迁移的数据进行验证,表明我们的数据迁移成功。

在某些应用场景下,例如缓存数据,并不是应用强依赖的,在缓存里获取不到数据,可以回源到数据库获取,因此在这种场景下通过容量评估,数据库可以承受回源导致的压力增加,就可以避免迁移旧数据。

在另一种场景下,数据一般是具有时效性的,应用在双写期间不断向新的集群中写入新数据,历史数据会逐渐过时,并被从旧的集群中删除,在一定的时间流逝后,新的集群中自然就有了最新的数据,就不再需要迁移历史数据了,但是这需要进行评估和验证。

(3)切读

把应用层所有的读操作路由到新的数据集群上,如下图所示。

image

在这一步骤里,把应用中读取的操作的数据源转换成新的数据集群,这时应用的读写操作已经完全发生在新的数据库集群上了。这一步一般不需要上线代码,我们会在一开始上双写时就实现开关逻辑,这里只需要将读的开关切换到新的集群即可。

(4)下线双写

在这一步,我们把写入旧的集群的逻辑下线,如下图所示。

image

这一步通常是在双写和切读后验证没有任何问题,并保证数据一致性的情况下,才把这部分代码下线。同时可以把旧的分片下线,如果是扩容的场景,并且重用了旧的分片1和分片2,则还可以清理分片1和分片2中的冗余数据。

对于字段迁移来讲,我们除了新增字段,双写后替换原来字段,我们还可以采用原地替换的方法,对于新数据加密,加密后做标志,然后异步的将历史数据加密,让查询程序兼容加密的数据和非加密的数据。

可以用“软着陆”来形容双写迁移方案,这和新领导上任后,一般先招心腹,慢慢的替代老下属的职责,慢慢淘汰老下属,慢慢实现软着陆如出一辙。

停机迁移

停机迁移的方法比较简单,通常分为停止应用、迁移旧数据、更改数据源文件、启动应用这4个步骤,如下图所示。

image

具体的迁移步骤如下。

  1. 停机应用,先将应用停止服务。
  2. 迁移历史数据,按照新的规则把历史数据迁移到新的缓存集群中。
  3. 更改应用配置,指向新的缓存集群。
  4. 重新启动应用。

这种方式的好处是实现比较简单、高效,能够有效避免数据的不一致,但是需要由业务方评估影响,一般在晚上交易量比较小或者非核心服务的场景下比较适用。

如何验证迁移成功

迁移过程中最重要的一环就是如何验证迁移方案是成功的,我曾经给小伙伴们定了一个顺口溜。

要明确什么样角色的什么人在什么时候到什么系统看到什么样的数据,才能确认迁移成功。

虽然这是看似简单的一句话,但是信息量满满的,有了这句话作为指导,能够确保迁移方案的方向一定是正确的,一定不会导致太大的问题。

这句话具体包括几个要素:谁、什么时候、什么样的数据,这些必须在迁移方案中都要事先明确,具体执行的人必须了解清楚这些条款,假设我们在迁移支付行业中的计费模板,那么迁移切量后,我们就需要计费的运营在切量后的第一时间到计费系统中查看计费的准确性,并且明确什么样的计费结果是准确的,什么样的计费结果是不准确的。

有的时候只靠人来确定数据是否正确恐怕还不够,我们通常需要工具自动化的进行比对,同样我们以计费模板迁移为例,我们从一套计费模板迁移到另外一套计费模板中,通常在初期我们不会真正的以新模板为准,我们会在程序中实现“双计”,既使用老的计费模板计费,也使用新的计费模板计费,在初期我们以老的计费模板为准,新的计费模板计费的结果只用于比对,并且计入日志,这样通过程序自动比对一段时间以后,发现新的计费模板确实没有问题,我们才真的开启开关,以新的计费模板为准。

当然,有的时候数据量巨大,我们比对每一个流量产生的数据不太现实,也会严重影响性能,这个时候我们需要对数据比对进行抽样,只对一些比较有代表性的数据进行比对。

系统迁移后数据清洗

系统迁移过程中,上了双写以后,历史数据仍然保留在老系统,因此,我们需要将历史数据迁移到新系统,因为,有些时候我们需要读取或者修改历史数据,例如支付行业的退款等。

我们把迁移历史数据的过程称为洗数据,通常使用如下的步骤来实现。

  1. 先清洗历史数据,将清洗后的数据写入新系统。
  2. 做全量对比,如果数据太多,没法全量对比,就抽样对比,看看有没有不一致的数据。
  3. 在数据量巨大的情况下,线上系统复杂,出现少量的不一致是正常的,这时候不一致的数据进行分析,这时候可能需要参考线上服务系统的交易日志,查明造成不一致的原因,并进行修复。

这里,有读者会对上面第2步有疑问,为什么会产生不一致的数据呢,这有很多原因。下面我们来仔细分析。

对于增加记录,到迁移历史数据这一阶段,我们使用的是双写,因此,数据在新老数据库中都会存在,即使双写有问题,导致一方不存在,我们也可以通过比对来补齐。

对于更新数据,则容易产生不一致,导致新老数据库的数据不一致,假设在迁移某条历史数据的过程中,线上的交易系统正好修改了这条数据,在双写系统还没有更新历史数据的时候,迁移工具已经把这条数据拿到了应用系统中,这时数据在新老库中都更新了,但是迁移工具后续又把老版本的这条数据更新到新系统中,就导致数据不一致。

对于删除数据,和上面更新数据有一样的问题,也会导致不一致的问题,这时候以谁为准,怎么保证一致性是个难题,我们需要借助修改的 timestamp 或者版本来区分哪一个是最新的版本,然后对数据进行修复。还有另外一个更好的方法,对于某些业务,数据是有一定时效性的,超过一定时间的数据就不再更改,因此,我们可以让双写的时间拉长,要长于数据更新的时间段,这样在历史数据完全不被更新的时候,我们再进行洗数据,就不会因为迁移而产生不一致了。

迁移失败怎么办

这里探讨,迁移失败了,迁移后验证迁移有问题了,怎么办?其实,迁移失败了没有什么好的办法,只有两个途径可以走,一个就是迁移失败了就在新系统中修复,但是有些时候这可能会导致更多不可用时间,另外一个方案就是迁移失败了,需要迁移回来,但是迁移回来迁移过程中产生的数据怎么办?

这是唯一能用的两个办法,没有更好的办法能解决迁移失败的问题,因此,在这个问题上我们不能奢求完美的解决方案,我们只能退而求其次,提前做好应对方案。

如果我们决定了迁移失败了就在新系统中修复,而不再切回老的系统,我们就要充分的做好应急方案,一旦这种事情出现了,我们要预测可能产生的问题,针对问题做相应的解决方案,甚至我们提前做好工具,在问题出现的时候,我们要快速发现和快速恢复。

如果我们决定了迁移失败后要迁移回来老系统,我们也要提前做好应急方案,应急方案中要包含如何发现问题,如何迁移回老系统,将流量迁移回老系统之后,还要考虑在新系统中遗留的数据怎么办,通常来讲,我们有两个方法,一种方法是通过工具把这些数据迁移回老系统,另外一种方法是让老系统兼容新数据。

迁移方案的评审

这里我们详细阐述作为架构师应该如何评审迁移方案。

首先,需要有人牵头写即将要评审的迁移方案,迁移方案的内容要包括具体的迁移产品,迁移的目的是什么,描述为什么要进行此次迁移,以及要达到什么效果,迁移任务的时间点,迁移方案什么时候在系统上实施,什么时候真正的进行切量,什么时候进行验证,什么时候结束,迁移过程中有什么原则,迁移会影响多少用户和商户,影响到什么程度,这次迁移的主要负责人是谁,参与人是谁,这些都需要落实到纸质的文档。

然后,我们最需要考虑的就是这次迁移的影响范围,都对哪些角色的人会产生影响,以及对哪些人是透明的,这要评估是否对商户、用户、运营人员等有感知,如果对任何人有感知,需要制定提前通知的方案,要通知哪些人,什么时候通知,以什么形式通知,是否需要被通知人回复认可等。

接下来需要确认迁移用户和商户的选择、顺序、批次,一般选择不重要的用户和商户先迁移,验证迁移过程没问题,再迁移重要的用户和商户,要综合考虑用户和商户的等级、交易类型、迁移复杂度等,以此确定用户和商户迁移顺序和批次。

通常,在全量切换之前,我们需要进行多次验证,我们需要在准生产测试迁移变更逻辑和开关逻辑,或者通过 TCPCopy 环境来验证代码变更的正确性,然后,通过少量的内部用户在线上验证逻辑的正确性,最后,会按照我们选定的用户和商户的批量,逐渐的放量、观察和验证,最后再全量切换。

然后,要确定迁移过程中对系统的变更内容,要确定变更的关键内容,然后做测试方案,要测试到所有的场景,包括迁移开关的开和关,要测试迁移失败后迁移回老系统的情况,不要抱着侥幸态度就忽略这部分的测试。

最后,进入迁移方案的关键内容,要对迁移的过程识别风险,这包括交易风险、业务风险、系统风险、技术风险和政策风向等,要对迁移过程中更改的信息流和资金流进行详细评估,对识别的风险要给出应对方案。

迁移开关和迁移工具

在迁移的过程中,除了要配合迁移开发系统,还有两个比较特殊的工作。

一个就是迁移开关的设计,在迁移的过程中,双写、切流量、有问题了切回流量,这些都需要使用迁移开关,迁移开关的设计非常的重要,如果迁移开关设计的不合理会产生很大问题,甚至会导致资金损失,在我经历过的金融系统中,曾经经历过迁移开关设计在统一的共享缓存上,由于网络原因重试导致请求流量重复,请求流量走到了不同的应用节点上,不同应用节点读取共享缓存有时差,导致两个流量一个走了开的逻辑,一个走了关的逻辑,如果后端系统没有做幂等,这会导致资金损失。因此,我们对迁移开关的设计,制定了如下的最佳实践。

  1. 迁移开关要做在订单上,在订单上标记是否迁移新系统。
  2. 迁移开关要有不同的维度,可在订单上,可在商户或者用户上,可在系统级别。
  3. 迁移开关要能开能关,也就是流量要能切到新系统,也要能切到老系统。

另外一个重要的任务是开发迁移工具,比如说迁移后校验数据,对比数据等,这都需要开发专业的工具,靠运营对比大量的数据很容易产生误差,如果想做好迁移,就要舍得成本来投入到这些关键任务上。

迁移过程中的政治因素

迁移是个费力不讨好的工作,因此,很多人其实不愿意干这个活,迁移做得好,那是应该做的事情,迁移出现了问题,那全是工作没做好,况且迁移总不会顺顺利利的,这就是为什么大家不愿意做,越是有挑战的任务,其实,它的内在价值就越大。

但是要真的想做一次彻底的迁移,替换掉老的系统,这需要一定的激励措施,要与迁移负责人和参与人明确迁移的目标和价值,一旦按照计划迁移成功除了迁移过程中给大家带来的经验,还有什么样的奖励,否则,只是作为一个常规任务,那么参与者必然失去兴趣,迁移也就成了挠痒痒,基本就变成今天迁移一点流量,明天迁移一点流量,最后就新老系统并存,不伦不类的。

迁移一定要由架构组来把关

数据迁移并不是一项需要高大上的技术工作,它需要的是对业务逻辑的把控,对操作流程的理解,对新旧系统特性和环境的掌握,以及对细节的掌控,要深入骨髓般的理解系统才能做好,因此迁移是需要架构组来把关的。

但是,迁移的事情也不是那么简单的,也不能由运营单独搞定的,这涉及到迁移工具的开发,迁移后的验证,迁移失败如何迁回,脏数据如何处理,迁移过程中如何平滑过度,例如迁移计费模板的过程,应该开发工具进行对比结果后,才能真正的使用新的计费系统,这些都是必须有业务人员和技术人员来共同完成的,因此,迁移工作最好由架构组牵头,由产品、运营、技术等一起来实施。

如何汇报迁移技术方案

由于迁移是个非常大的任务,设计的部门和角色比较多,由于出了问题产生的影响比较大,因此,很多老板都会关注迁移的方案,这里,对于迁移负责人和架构师,要负责给不同角色的人汇报迁移方案,这里我总结的一个最佳实践是不同角色的人关注不同的内容。

  1. 销售的老板会关注迁移后带来的价值,带来哪些系统能力的提升,能够带来多少毛利的增长。
  2. 业务的老板关注的是业务的风险,关注迁移以后是否带来产品能力的提升等等。也会关注成本的降低;还会关注迁移的里程碑,时间点等。
  3. 运营的老板更会关注迁移的实施方案,包括如何通知商户、如何选择商户等。
  4. 而技术的老板会关注具体的迁移设计方案本身。

因此,在给不同的老板做汇报的时候,我们汇报内容的侧重点要有所不同。


《可伸缩服务架构:框架与中间件》是《分布式服务架构:原理、设计与实战》的姊妹篇。

两本书结合后可覆盖保证线上高并发、高可用服务的各个方面:一致性、高性能、高可用、可伸缩、可扩展、敏捷性。

支付平台架构系列
Web note ad 1