反思跨系统同步数据的方法

最近在公司接触了一种业务场景,感觉现有设计与实现都不怎么靠谱,在这里简单记录一下。

背景

在一个经过业务拆分的分布式环境里,经常需要跨系统进行一些数据的同步,这种情况经常伴随着数据冗余。

这里我举个非常简单的例子(例子有点不切和实际,但是一定会存在类似场景):

商品和品牌,是两个独立系统各自维护的,数据库隔离。

商品数据库,维护了商品关联了哪个品牌ID,出于某种原因商品数据库冗余存储了品牌的名字。

当在品牌数据库修改品牌名称时,应当触发商品数据库修改所有关联了该品牌ID的商品记录,更新其中的品牌名字。

有问题的方案

现在公司为了这种场景,实现了一个公共服务叫做事件服务,你可以理解就是一个队列,每次发生变更,可以丢一条事件进去。

第一个问题就是,修改了品牌名字,再产生事件,这两个步骤是没有事务的,所以事件可能会丢。

解决第一个问题的思路是:每次都开启一个事务,先修改品牌名字,在同一个事务里在品牌数据库中生成一条事件记录,这样事件记录就不会丢失,同时我们也没有理由再使用事件服务了。

这样做,只能保证事件可靠落地,但是还需要将事件通知给商品服务,主动推送意味着还是有可能失败,解决推送失败的方法就是过一会重新推送,我们可以在事件表里维护一个完成标记位,的确可以实现重试推送。

但是这样又引入了一个新的问题,就是重试推送就会导致事件乱序,假设先产生的事件推送失败,而后产生的事件推送成功,当失败的事件最终推送成功后,商品库保存了一个旧的数据。

对于绝大多数系统来说,跳过一个中间修改步骤直接执行后续的修改,会导致数据完全错乱,即便先前的修改最终也抵达了对方系统。所以,我们必须做到2个系统间的修改路径和时序完全相同才行。

所以,一个改进的实现就是:事件必须按产生的次序逐个推送,即前一个事件对方反馈完成,后一个事件才能推送过去。

这里有另外一个问题,就是商品库要批量修改大量的关联商品记录,是一个耗时的过程,那么Push模型就显得很麻烦。

建议的方案

基于上述分析,我建议的方案是商品库主动来品牌库pull事件,自行维护当前消费到哪个事件ID即可。

总体来说,一个是通过数据库事务保障事件的可靠落地,一个是采用拉模型保障事件的有序抵达与执行。

对于同一个事件来说,一般都可以幂等性的多次执行,这是商品库需要保证与实现的事情;而品牌库要保证的是,每个事件可靠落地,按序排列,等待被查询即可。

上面的例子是一个很简单的场景,真正对品牌的编辑会一次性修改很多信息,包括与品牌关联的1:N关系表;这些修改都应该在事务中完成,所有的变更都应该作为事件记录落地,产生一条还是多条事件我认为都是可以的。

发表评论

电子邮件地址不会被公开。