如何对遗留系统的数据库进行拆分
在上述几个改造场景中,有些步骤涉及对遗留系统的数据库进行拆分。那么在多服务共享数据库的情况下,如何决定首先拆分哪个服务的数据库?哪种拆分顺序的工作量最小呢?一种方法是采用数据“读依赖最小”的顺序进行拆分,这种方法的实施可以概括为以下三个步骤:
1.表拆分,解除服务之间的数据关系耦合:列出各个服务对表的读写关系,如果只有一个服务对某个表进行写操作,不用拆分该表;当有多个服务对某个表进行写操作时,首先考虑将这张表拆分成多张有连接关系的表,将其转化为被某个服务单独进行写操作的表。
2.绘制服务依赖关系图:在每张表被独立服务单独进行写操作的情况下,按照如下规则构造有向图,即将所有服务作为有向图中的点,当服务A需要从服务B的数据库读取数据时,画一条由B指向A的有向边,表示服务A依赖于服务B;依此类推,直到绘制完整个依赖关系图。
3.库拆分,解除服务之间的数据库耦合:以有向图中各节点的出度(即从节点出发的边的条数)作为该服务被依赖数,进行排序,挑选被依赖数最小的服务,首先对其进行数据库解耦,把该服务下的数据库表独立出来,并在该服务里提供数据接口,以供依赖于它的服务调用。
重复第3步,直到所有数据库被拆分为由各个服务独享的数据库。
例如,如图6-12所示,是一组包含四个服务的依赖关系图,服务右上角的角标表示该服务的被依赖数。得知短信服务的被依赖数为0,为当前被依赖最少的服务,可以首先将该服务的数据库拆分出来。其次再按顺序依次拆分积分服务、订单服务和账户服务的数据库,直至所有数据库被拆分为由服务所独享的数据库。
四、遗留系统改造案例
本节以笔者曾亲身参与改造的遗留系统为案例,介绍如何将一个大型遗留系统向微服务架构进行迁移。
1. 改造前的系统情况
该系统是一个用于提供全球房产信息搜索服务的大型门户平台,核心功能主要包括如下几点,如图6-13所示。
- 为用户提供基于地理位置、关键字的房产信息搜索功能。
- 提供基于定制搜索条件的房源信息订阅。
- 提供桌面端、平板端、手机端等多种不同方式的访问。
该系统是从多年前收购的一个通用搜索平台改造而来的,整体为一个规模庞大的单体应用,使用同一个代码库,技术栈主要以Java为主,数据库为Postgre,搜索引擎使用FASTSearch(历史原因),代码量大约在300万行左右。
随着业务演进的速度越来越快,单体应用的弊端导致系统变更成本越来越高。即使修改几行代码也需要经过冗长的测试以及发布流程,系统发布才能生效。面对这些问题,团队决心使用微服务架构,将未来的新功能全部以微服务实现,并将系统原有功能逐步迁移到微服务架构下。
2. 改造过程
步骤1:通过遗留系统API提供数据
当时,研发团队接到的第一个特性需求如下所示:
- 支持NativeApp提供核心功能
- 三个月上线首个版本
此时,面临的挑战主要包括以下两个:
- 该系统的主要研发成员在海外,国内团队刚成立,新人多,知识迁移成本高。
- 系统本身是基于商业通用搜索平台上进行的二次开发,逻辑复杂,代码耦合度高,变更成本高。
采取的方案主要包括如下几部分:
- 在遗留系统上封装现有逻辑,提供基础API。
- 重新实现一个服务,类似之前章节提到的BFF模式,为NativeApp提供数据。
- 该服务通过访问遗留系统提供的基础API获得所需要的数据。
- 该服务内实现必要的转换,提供给NativeApp界面进行呈现。
利用微服务架构的异构性,新服务命名为AppService,使用RubyOnRails实现,并实现相关的数据转换逻辑,为NativeApp提供数据API。通过这种方式实现的新服务,不仅能满足NativeApp的需求,而且可以快速开发,独立部署。
改造后的系统架构图,如图6-14所示。其中AppService服务提供两组API,分别是提供房源信息的EstateAPI和提供地理位置信息的LocAPI。