1. 首页
  2. 科技部落

分布式事务消息最终一致性方案在迁移数据库业务中的实践

一、业务场景

在分库分表业务中,通常使用数据表中的某一个字段作为路由,而此次迁移的数据,多个字段都可能做为查询条件被查询,我们采用了将多个维度各存储一份数据的方案解决,因此,在SQL server中的一个表,被拆散到MySQL数据库中的多张表,并且数据需要被按照多个维度复制多份。

参考图一,使用图形化的方式展示此次迁库的业务场景,归纳起来,就是将原来应用只对一个数据库操作一份数据,变成了应用同时需要对多个数据库多份数据同时进行操作的场景。

分布式事务消息最终一致性方案在迁移数据库业务中的实践
图一 业务场景描述

二、方案介绍

在进行方案设计之前,我们梳理了主要的业务需求:

1. SQLServer和MySQL数据库双写,需保证数据最终一致性,且需要尽快达成一致状态。

2. 在迁移开始前SQLServer做为主要数据库,向MySQL数据库同步数据;迁移后,在一段时间内,MySQL数据库做为主要数据库,向SQLServer数据库同步数据,以支持回滚方案。

3. 须平滑迁移

4. 代码侵入性尽量小

我们设计了多种方案,并对这些方案进行了选型。

表一 方案及选型

方案需求满足度
XDATA线下一次性迁移需停机迁移(不满足需求3)
canal异步同步不能满足双向同步的需求(不满足需求2)
同步双写+定时检查从库可能在较长时间内数据和主库不一致(不满足需求1)
事务消息系统可满足需求

如表一所示,经过方案设计和选型,我们最终采用独立的基于事务消息的最终一致性方案来实现。

这个基于事务消息的系统,我们命名为MST,MST系统主要包含两部分:

l 独立的事务消息系统MST,包括独立的MST数据库表,相关接口和定时任务

l MST客户端,一个Spring Boot starter包,包含业务方代码中作为的主动方或被动方需要用到的各种工具和annotation

2.1  独立的事务消息系统MST总体流程

分布式事务消息最终一致性方案在迁移数据库业务中的实践
图二 MST系统流程图

2.2 MST系统流程说明

MST系统中会涉及多个数据库之间的数据同步,我们把操作主要数据库的那个应用称作主动方,其他参与数据同步的应用,称作被动方。回顾图一介绍的业务场景,SQLServer做为主要数据库,向MySQL数据库同步数据的阶段中,操作SQLServer数据库的应用,称作主动方,操作MySQL数据库应用称作被动方。

假设主动方对数据进行更新操作,MST执行的流程如下:

  • 主动方流程

a. 主动方首先调用MST系统“接受预发送消息“接口将更新操作保存到MST系统的数据库中(即插入一条状态为“待发送”的事务消息记录)

b. 主动方执行具体的业务更新逻辑(事务执行更新投标表插入本地事务记录表)

c. 执行完成后再调用MST系统“主动方业务确认完成“接口将刚刚插入MST数据库中的那条状态为“待发送”的事务消息记录更新为“已发送”状态,并发送该条事务消息到消息系统(如Kafka)

至此,主动方操作完成。

其中步骤a和c 不需要在主动方硬编码完成,通过在步骤b的方法上加上MST客户端的@MstProducer标签通过AOP的方式完成。同时步骤b中打开本地事务并在业务操作(更新投标记录)后插入本地事务记录然后关闭本地事务的操作也由MST客户端完成。主动方应用的研发同学只需要关注和实现自己的业务更新逻辑即可。

  • 被动方流程

被动方通过消费消息异步完成向其他数据库同步数据的动作。

d. 被动方应用消费消息

e. 被动方执行具体的业务更新逻辑(事务执行更新投标表插入本地事务记录表)

f. 执行完成后再调用MST系统“被动方业务确认”接口,将相应的MST数据库中的那条状态为“已发送”的事务消息记录更新为“已完成”状态

至此,所有的操作完成。

同主动方一样,被动方的研发同学只需实现自己的更新逻辑,插入本地事务记录表和步骤f的操作全部由MST客户端的@MstConsumer标签通过AOP的方式完成。

上述流程,保证了正常情况下主动方和被动方数据的一致性,MST数据库中状态为“已完成”的记录代表主动方和被动方一定都已经完成了数据同步而达到了一致。

2.3 异常情况

  • 如果一段时间内(超时时间)有些记录的状态一直是“待发送”或“已发送”怎样保证这些操作的一致性?

我们一起分析什么情况下状态会一直是“待发送”或“已发送”:

a) 待发送:即图二中步骤2完成, 应用在步骤3/4/5/6可能出现异常

b) 已发送:即图二中步骤6完成, 应用在步骤7/8/9/10/11可能出现异常

通过本地事务记录表我们可以判断刚刚这条操作在本地到底有没有被执行。因此我们在主动方和被动方分别提供一个接口通过业务id(biz_id)反查某个业务到底有没有执行。MST系统则设计了两个定时任务分别轮询MST数据库中状态长时间为“待发送“和“已发送”的事务消息记录,分别调用主动方和被动方提供的“业务逻辑完成验证”接口,对数据一致性做补偿操作。

  • 补偿失败问题

如果被动方始终失败,怎么处理?一般的情况下,有可能需要人工介入,不过在现在是数据同步的业务场景,我们可以将人工干预的步骤换成自动强行同步两边的数据即可。

2.4 MST系统表设计

本地事务表local_mst_transaction主要的字段参考表二,该表需要在每个参与分布式一致性的数据库中都创建一份:

表二 local_mst_transaction数据表结构

mst_id每条更新或插入SQL操作对应MST系统有一个唯一的id
biz_id每次更新或插入操作对应唯一业务id
app_id主动方或被动方的应用id
shard_id分库分表路由id,方便到具体的分表中反查

MST数据库中事务消息记录表message_transaction主要字段参考表三:

表三 message_transaction数据表结构

mst_id每条更新或插入SQL操作对应MST系统有一个唯一的id
biz_id每次更新或插入操作对应唯一业务id
sub_biz_id一个sql更新多条数据时,每条更新数据记录对应的子业务id
message_body具体业务操作数据
producer_app_id主动方的应用id
consumer_app_id被动方的应用id
mq_topicMQ的topic
status状态:待发送0, 已发送1,  已完成2, 失败-1
producer_shard_id主动方分库分表路由id
consumer_shard_id被动方分库分表路由id
producer_retry_time主动方重试次数
consumer_retry_time被动方重试次数

2.5 相关配置

每个mstId在MST系统中都有相应的配置以保证流程的正常进行,

例如mstid=42,它的配置如下

{

    “mstId”: “42”,

    “producerAppId”:”1000002006″,

    “producerCheckUrl”:”http://mock0/mst/checkProducerBizDoneBidListing”, 

    “mqTopic”:”InvestBidMst”,

    “consumers”: [

        {

            “consumerAppId”:”1000002006_1″,

            “consumerCheckUrl”:”http://mock1/mst/checkConsumerBizDoneBidLender”,

            “shardFiled”:”lenderId”

        },

        {

            “consumerAppId”:”1000002006_2″,

            “consumerCheckUrl”:”http://mock2/mst/checkConsumerBizDoneOldBid”

        },

        {

            “consumerAppId”:”1000002006_3″,

            “consumerCheckUrl”:”http://mock3/mst/checkConsumerBizDoneOldBid”

        },

        {

            “consumerAppId”:”1000002006_4″,

            “consumerCheckUrl”:”http://mock4/mst/checkConsumerBizDoneBidSupplement”

        }

    ]

}

producerCheckUrl为主动方反查接口url。被动方可以是多个,配置在consumers,其中consumerCheckUrl为被动方反查接口url。

三、实践中遇到的问题和解决方案

3.1 乱序问题

当多个更新操作之间需要有序执行的时候,主动方可以保证有序性,可是被动方由于是通过消息系统异步解耦的,不能保证严格的顺序。我们的解决方案是通过业务规则来规避乱序的风险。

3.2 批量更新问题

这里的批量更新是在一条SQL语句更新多条的情况,例如,主动方对SQL Server中的数据以一个或多个条件更新,在SQLServer数据库中只需要一条SQL语句,而这条SQL语句在被动方,可能无法通过一条SQL执行,需要拆分。我们的解决方案是,先锁住数据(根据MySQL和SQLServer数据库事务隔离级别和内部机制不同采用了不同的做法),查询出所有需要被更新的行,然后逐条更新的方法来实现。这种方案拉长了事务的执行时间,有长时间锁库的风险,综合考虑了我们的业务场景,此方案带来的风险在可接受范围。

四、总结

以上简单介绍了利用MST事务消息系统在数据库迁移和分库分表多维度数据同步业务中的实践。MST事务消息系统除了应用在数据同步业务场景外,其本身也可应用于分布式事务业务场景和需要可靠消息的业务场景。MST系统还处于不断完善和发展中,后续我们会在代码侵入性、总体性能和鲁棒性等各方面继续优化。

欢迎各位感兴趣的朋友与我们交流,如果有纰漏或者不足的地方,欢迎指正或者给予补充。

本文来自拍码场,经授权后发布,本文观点不代表信也智慧金融研究院立场,转载请联系原作者。