微服务下的幂等设计
二、新增,insert into user(userid,name) values(1,'a'),
如 userid 为唯一主键,即重复操作上面的业务,只会插入一条用户数据,具备幂等性。如 userid 不是主键,可以重复,那上面业务多次操作,数据都会新增多条,不具备幂等性。
三、修改,区分直接赋值和计算赋值。
1、直接赋值,update user set point = 20 where userid=1,不管执行多少次,point 都一样,具备幂等性。2、计算赋值,update user set point = point + 20 where userid=1,每次操作 point 数据都不一样,不具备幂等性。
四、删除,delete from user where userid=1,多次操作,结果一样,具备幂等性。
上面场景中,我们发现新增没有唯一主键约束的数据,和修改计算赋值型操作都不具备幂等性
那怎么去解决呢?
网上介绍很多,但介绍的太简单了,且关键点都没有介绍到。老顾这里只介绍常用的方案
token 机制
token 方式的流程,上一张图,比较清晰
上图就是 token+redis 的幂等方案,适用绝大部分场景。主要思想:
1、服务端提供了发送 token 的接口。我们在分析业务的时候,哪些业务是存在幂等问题的,就必须在执行业务前,先去获取 token,服务器会把 token 保存到 redis 中。(微服务肯定是分布式了,如果单机就适用 jvm 缓存)。2、然后调用业务接口请求时,把 token 携带过去,一般放在请求头部。3、服务器判断 token 是否存在 redis 中,存在表示第一次请求,可以继续执行业务,执行业务完成后,最后需要把 redis 中的 token 删除。4、如果判断 token 不存在 redis 中,就表示是重复操作,直接返回重复标记给 client,这样就保证了业务代码,不被重复执行。
这种方案是比较常用的方案,也是网上经常介绍的,但是有一点不同的地方:
网上方案:检验 token 存在(表示第一次请求)后,就立刻删除 token,再进行业务处理上面方案:检验 token 存在(表示第一次请求)后,先进行业务处理,再删除 token
关键点就是 先删除 token,还是后删除 token。一、网上方案缺点
我们看下网上方案,先删除 token,这是出现系统问题导致业务处理出现异常,业务处理没有成功,接口调用方也没有获取到明确的结果,然后进行重试,但 token 已经删除掉了,服务端判断 token 不存在,认为是重复请求,就直接返回了,无法进行业务处理了。
二、上面方案缺点
后删除 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 的生成,可自行查阅。如果是分库分表场景下,路由规则要保证相同请求下,落地在同一个数据库和同一表中,要不然数据库主键约束就不起效果了,因为是不同的数据库和表主键不相关。因为对主键有一定的要求,这个方案就跟业务有点耦合了,无法用自增主键了。
去重表机制
这个方案业务中要有唯一主键,这个去重表中只要一个字段就行,设置唯一主键约束,当然根据业务自行添加其他字段。主要流程上图
上面的主要流程就是 **把唯一主键插入去重表,再进行业务操作,且他们在同一个事务中。**这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避免了幂等问题。
这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性。
这个方案也是比较常用的,去重表是跟业务无关的,很多业务可以共用同一个去重表,只要规划好唯一主键就行了。
总结
上面介绍了一些幂等方案,小伙伴们根据自身的业务进行选择,尽量不要让系统变的复杂,所以推荐唯一主键和乐观锁方式,因为实现比较简单。好了,今天就介绍到这里,谢谢大家!!!
评论