微服务架构之幂等性问题及设计思想,你不得不知的一些幂等方案

微服务架构之幂等性问题及设计思想,你不得不知的一些幂等方案

前言

小伙伴们有没有遇到过生产环境常常出现太重复的数据?在排查问题的时候,数据又是正常的。这个是何解呢?怎么会出现这种状况,并且还很难排查问题。今天我给你们分享一下这里的缘由,以及解决方案。redis

罪魁祸首

产生重复数据或数据不一致(假定程序业务代码没问题),绝大部分就是发生了重复的请求重复请求是指同一个请求由于某些缘由被屡次提交。致使这个状况会有几种场景sql

1)微服务场景,在咱们传统应用架构中调用接口,要么成功,要么失败。可是在微服务架构下,会有第三个状况【未知】,也就是超时。若是超时了,微服务框架会进行重试。2)用户交互的时候屡次点击。如:快速点击按钮屡次。3)MQ消息中间件,消息重复消费4)第三方平台的接口(如:支付成功回调接口),由于异常也会致使屡次异步回调 5)其余中间件/应用服务根据自身的特性,也有可能进行重试。数据库

咱们知道了发生的缘由,本质就是屡次请求了,那如何解决呢?缓存

幂等性

有些小伙伴们会想到幂等这个词,是的,就是咱们在设计某些接口时,要考虑如何保证接口幂等,那什么是接口幂等呢?bash

网上是这样介绍的【接口的幂等性实际上就是接口可重复调用,在调用方屡次调用的状况下,接口最终获得的结果是一致的服务器

网上的说法定义,有点不是太正确,咱们看下怎么不正确架构

一个线程请求用户列表接口:select * from user,返回用户表中的数据,而另外一个线程往用户表插入数据。那请求用户列表的线程返回的数据每次都不同,那按照上面的说法,查询用户列表的接口就不是幂等的,这显然是不正确的。框架

老顾的理解应该是屡次调用对系统的产生的影响是同样的,即对资源的做用是同样的,可是返回值容许不一样。异步

幂等场景

咱们来看一下SQL相关业务是否幂等?jvm

1、查询,select * from user where xxx,不会对数据产生任何变化,具有幂等性

2、新增,insert into user(userid,name) values(1,'a'),

userid为惟一主键,即重复操做上面的业务,只会插入一条用户数据,具有幂等性。如userid不是主键,能够重复,那上面业务屡次操做,数据都会新增多条,不具有幂等性

3、修改,区分直接赋值和计算赋值。

一、直接赋值,update user set point = 20 where userid=1,无论执行多少次,point都同样,具有幂等性。二、计算赋值,update user set point = point + 20 where userid=1,每次操做point数据都不同,不具有幂等性

4、删除,delete from user where userid=1,屡次操做,结果同样,具有幂等性。

上面场景中,咱们发现新增没有惟一主键约束的数据,和修改计算赋值型操做都不具有幂等性

那怎么去解决呢?

网上介绍不少,但介绍的太简单了,且关键点都没有介绍到。老顾这里只介绍经常使用的方案

token机制

token方式的流程,上一张图,比较清晰

微服务架构之幂等性问题及设计思想,你不得不知的一些幂等方案

上图就是token+redis的幂等方案,适用绝大部分场景。主要思想:

一、服务端提供了发送token的接口。咱们在分析业务的时候,哪些业务是存在幂等问题的,就必须在执行业务前,先去获取token,服务器会把token保存到redis中。(微服务确定是分布式了,若是单机就适用jvm缓存)。二、而后调用业务接口请求时,把token携带过去,通常放在请求头部。三、服务器判断token是否存在redis中,存在表示第一次请求,能够继续执行业务,执行业务完成后,最后须要把redis中的token删除。四、若是判断token不存在redis中,就表示是重复操做,直接返回重复标记给client,这样就保证了业务代码,不被重复执行。

这种方案是比较经常使用的方案,也是网上常常介绍的,可是有一点不一样的地方:

网上方案:检验token存在(表示第一次请求)后,就马上删除token,再进行业务处理上面方案:检验token存在(表示第一次请求)后,先进行业务处理,再删除token

关键点就是 先删除token,仍是后删除token。

1、网上方案缺点

咱们看下网上方案,先删除token,这是出现系统问题致使业务处理出现异常,业务处理没有成功,接口调用方也没有获取到明确的结果,而后进行重试,但token已经删除掉了,服务端判断token不存在,认为是重复请求,就直接返回了,没法进行业务处理了。

2、上面方案缺点

后删除token也是会存在问题的,若是进行业务处理成功后,删除redis中的token失败了,这样就致使了有可能会发生重复请求,由于token没有被删除

小伙伴们有没有发现,其实上面的问题就是数据库和缓存redis数据不一致的问题。以前老顾分享了一篇文章,里面详细介绍了如何解决数据库和缓存redis数据不一致的问题。小伙伴们可自行查阅。

其实根据这个场景的业务,能够有个简单的处理方式。老顾推荐是网上方案先删除token,先保证不会由于重复请求,业务数据出现问题。顶多再让用户处理一次。

出现业务异常,可让调用方配合处理一下,从新获取新的token,再次由业务调用方发起重试请求就ok了

token机制缺点

小伙伴们有没有发现,业务请求每次请求,都会有额外的请求(一次获取token请求、判断token是否存在的业务)。其实真实的生产环境中,1万请求也许只会存在10个左右的请求会发生重试,为了这10个请求,咱们让9990个请求都发生了额外的请求。(固然redis性能很好,耗时不会太明显)

乐观锁机制

关于乐观锁老顾以前也讲过,你们能够去查阅。乐观锁这里解决了计算赋值型的修改场景。咱们对以前的sql语句进行修改。

update user 
set point = point + 20, version = version + 1
 where
userid=1
 and
 version=1
复制代码

加上了版本号后,就让此计算赋值型业务,具有了幂等性

乐观锁机制缺点

就是在操做业务前,须要先查询出当前的version版本

惟一主键机制

这个机制是利用了数据库的主键惟一约束的特性,解决了在insert场景时幂等问题。但主键的要求不是自增的主键,这样就须要业务生成全局惟一的主键,以前老顾的文章也介绍过分布式惟一主键ID的生成,可自行查阅。若是是分库分表场景下路由规则要保证相同请求下落地在同一个数据库和同一表中,要否则数据库主键约束就不起效果了,由于是不一样的数据库和表主键不相关。由于对主键有必定的要求,这个方案就跟业务有点耦合了,没法用自增主键了

去重表机制

这个方案业务中要有惟一主键,这个去重表中只要一个字段就行,设置惟一主键约束,固然根据业务自行添加其余字段。主要流程上图

微服务架构之幂等性问题及设计思想,你不得不知的一些幂等方案

上面的主要流程就是 把惟一主键插入去重表,再进行业务操做,且他们在同一个事务中。这个保证了重复请求时,由于去重表有惟一约束,致使请求失败,避免了幂等问题

这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个事务,即便业务操做失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性

这个方案也是比较经常使用的,去重表是跟业务无关的,不少业务能够共用同一个去重表,只要规划好惟一主键就好了。

总结

上面介绍了一些幂等方案,小伙伴们根据自身的业务进行选择,尽可能不要让系统变的复杂,因此推荐惟一主键和乐观锁方式,由于实现比较简单。好了,今天就介绍到这里,谢谢你们!!!

相关文章
相关标签/搜索