实现“幂等”的N种方案,你都学废了吗?

架构之道与术

共 1108字,需浏览 3分钟

 ·

2021-08-20 15:10

正如上篇所说,“网络超时”无处不在,所以客户端“超时重试”也无处不在,因为客户端会重试,就要求服务器提供的接口必须保证幂等,否则可能产生脏数据。


方案1:天然幂等

(0) 如果是一个delete操作,主键是外部传入的,重复delete,不会有脏数据。

(1) 如果是一个update接口,对db里面某1行数据进行覆盖操作,那天然幂等,覆盖多次,结果不变。但如果是累加/累减操作,比如对商品库存数字做扣减,就不会幂等。

(2)如果是一个insert操作,有主键,db对这个主键设置了唯一索引,那也会幂等。重复的数据,insert2次,第2次会抛错。

关键:这里的主键,必须是调用方生成。如果是被调用方生成,重复调用,被调方内部生成了不同的主键ID,就做不了幂等。


方案2:数据本身有状态字段,可以判断出是否重复调用

比如订单数据,订单的状态已经更改成了“已支付”,现在又来一个网络请求,对这个订单进行支付,那就知道,已经重了。


方案3:判重表

数据没有状态的情况下,就需要加一张额外的判重表。每次做业务逻辑之前,先查一下判重表,如果有了,说明这个请求之前已经处理过了,直接返回成功;否则就更新业务数据,同时把这个请求插入判重表。


方案4:布隆过滤器

判重表会越来越大,每个请求的处理都需要查询判重表。一个优化办法是为判重表加上一个布隆过滤器,布隆过滤器是在内存中。查询的时候,先查布隆过滤器,如果没有,说明这个请求不是重复调用,进行业务逻辑操作,然后把这个请求插入判重表 + 布隆过滤器; 

如果布隆过滤器存在,可能是误判,再次查询判重表确定这个请求是否真的已经处理过。处理过了,直接返回成功;没处理过,处理,同时插入判重表 + 布隆过滤器。

布隆过滤器虽然会误判(假阳性),但能挡绝大部分请求查判重表,已经很好的优化效果。


另外一个折中办法是:定期删除判重表里面的老数据,只保证请求在一段时间内幂等(比如1周)。2次重复请求如果跨越了这个时间段,仍然判断不出是重复操作。


方案5:递增的数据编号

如果请求有编号,严格的单调递增,那就不需要存判重表,只需要存当前已经处理的最大请求编号就够了。

最典型的例子就是消费Kakfa消息的幂等,消费者只需要在DB中存储已经消费过的最近offset。当挂了重启之后,从kafka拉取消息重新消费,所有

<= offset的消息,都直接过滤掉。


浏览 27
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报