如何解决系统的一致性问题

1.背景

当前后端开发的工作,时常会遇到一些数据同步的类型工作。这些工作看似简单,无非是数据接受方提供一个数据写入接口,数据发送方通过接口调用实现数据同步。当然大部分场景同步的数据需要进行清理及加工工作,所以一般会有一个专门的数据同步服务负责处理数据的清理,加工,上报。简易的流程图如下。
在这里插入图片描述

方案一:数据对账

对账是解决数据一致性对比的一个比较完善的方案。为什么这么说呢?因为它比较的是最终落库的数据是否一致。如果你想要通过对账的方式来保证数据一致性的话,需要做以下的这些工作。这方案不仅仅怀疑同步失败的时数据时不一致的,也怀疑同步成功时数据是不一致的。

  1. 需要一张表记录同步的记录,这个记录包括同步成功的和同步失败的,可以只记录关键信息,比如说同步会员信息的话,可以只记录会员id。
  2. 需要一个定时任务,执行时间需要避开数据变更的高峰时间,定时任务的执行评率可以根据需要对账的数据量的多少决定。
  3. 数据的发送方和数据接受方需要提供数据查询接口。因为在数据对账的过程中需要分别查询出发送方和接受方的数据进行对比。如果数据相同,表明数据是一致的,可以删除同步记录表中的这条数据。如果不相同,则需要重新进行一次同步动作,如果这次同步成功则删除同步记录表中的这条数据。不成功的话先不删除,下次定时任务执行时,可以重新拉取执行。

方案二:失败重试

与数据对账不一样,失败重试面向的同步失败的记录。这个思想认为如果数据接受方反馈数据接受成功,那么两方的数据就是一致的。数据接受方既然返回了成功的反馈,那么数据接受方就应该自己去保证数据一定会落盘。综上所述,如果数据接受反馈失败,或者调用数据接收方接口报错时,可以认为数据是不一致的,对于这种情况需要不断重试直至成功。
失败重试需要在下面的几个纬度进行衡量:

  • 是立即重试,还是过一段时间进行重试
  • 是否是无限制的重试,还是需要设置一个重试的次数
  • 重试时是重放失败时的请求,还是已回源数据的方式进行重试
  • 失败的场景有哪些:网络异常,服务端异常(业务异常,运行时异常),请求超时
  • 接受方的接口是否具有幂等性

有一问题是需要思考的?什么样的异常重试是有效的?

  • 短时的网络波动,短时间多次重试可以解决
  • 长时间的网络中断,间隔一段时间多次重试可以解决
  • 服务端异常,重试依然可能会异常,需要接收方核实异常原因
  • 请求超时场景,重试可能会成功,如果多次重试还是不行,需要找接收方核实原因,进行接口优化或者接口调用方调大超时间

重试失败时的数据,会产生ABA问题,因为重试时的数据有可能已经是旧数据了。这个时候应该回源DB中的数据进行重试。这个方案无论是消息发送端还是消息消费端都是可行的。
至于重试是手动的好,还是自动的好。手动的方式,不能及时的解决问题,如果可以做成自动的方式是最好的。自动的方式,需要考虑定时任务的执行时间间隔,以及最好限定下每次最多可以处理的消息数量。避免失败消息过多时对下游产生多大的压力。可以控制对下游的压力的情况下,重试任务的时间间隔可以设置的短一点。

具体方案

步骤一:调用数据同步接口时如果失败,立即重试,重试次数不需要太多,3次即可。可以使用spring-retry组件。需要考虑重试时,消息已经是旧消息了。
步骤二:如果步骤一重试成功可以不需要走下面的步骤。如果不成功,可以将失败请求保存进入到失败队列,如果数据量不大的话,失败队列可以使用redis实现。
步骤三:启动一个定时任务,每个小时执行一次即可,每次执行时它会从失败队列中取一定数量失败消息,重新上报。
步骤四:怎么保证重试上报的消息不是旧消息。回源数据的方式依然存在一定概率的以旧数据覆盖新数据问题,也是需要时序控制器来保证顺序性的。所以这里我建议还是使用请求重放的方式,可以减少回源的次数。
步骤五:时序控制器怎么实现呢?
使用redis实现,key值是“业务前缀 + 关键字”, value值是执行同步动作的“时间戳”。如果说会员信息新增场景:key:”USER_INFO_ADD:userId“, value: String timestamp = String.valueOf(System.currentTimeMillis());
重试时,使用请求带的时间戳T与redis中的时间戳t进行比较,如果比t大,则可以重试,重试成功将T写入redis中,失败的话可以继续重试或者放弃重试;如果比t小,则放弃重试。

如何设计一个较为通用的重试框架?
需要的特性:

  • 立即多次重试能力,可以参考Spring-retry框架
  • 异步的,间隔较长时间的重试能力
  • 保证重试请求的时序性,提供一个这样的可选项
  • 支持设定最大的重试次数
  • 多次重试失败的任务可以放到死信队列中, 方便用户查询,不再重试的请求
  • 支持手动重试死信队列中的请求

aop 中的 MethodInterceptor 如何使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值