实现接口幂等性的几种参考方案
下面的内容要是有不对的地方,麻烦私信我改掉哦。谢谢各位看官
分析
幂等性的含义
幂等性强调的是外界通过接口对系统内部的影响,
外界怎么看系统和幂等性没有关系,只要一次或多次调用对某一个资源应该具有同样的副作用就行。特别注意:是对资源造成的副作用必须是一样的,但是返回值允许不同!!!
分析数据库哪些地方会出现幂等性问题
- select:查询操作并不会产生或变更新的数据,因此查询是天然具备幂等性的。
- delete:这里有两种情况:物理删除和逻辑删除。
物理删除:这个删除只会进行一次,无论执行几次delete操作,造成的效果是一样的!是一个完美的幂等性操作。
逻辑删除:这种一般是通过修改数据中某个字段来表示删除的,只是用update修改字段而已,这种操作,你无论update多少次,造成的效果都是一样的!也是幂等性操作
- insert:这个主要看是带唯一索引和不带唯一索引。
带唯一索引的插入:重复插入是会报异常的!(当然也要看你的sql怎么写的,不同的写法可能不会报唯一键冲突的异常给你:
就像常规的INSERT INTO 表名(列名) VALUES(列值)这样的sql语句!是通过报异常的。
另外就是:MySQL提供了三组防止重复插入的语句,但是必须要有唯一索引才能用的:
1、insert ignore into:起冲突就忽略掉这个插入的数据。
2、replace:替换掉之前的,是先删除再插入。
3、on duplicate key update:存在就更新。)该操作是幂等性操作
不带唯一索引的插入: 随便你插入,总会成功的,这种情况就是一个非幂等性操作。
- update:这个要具体问题具体分析,看你是怎么个修改法,比如计算式的与非计算式的。
计算式的更新:这类操作是指UPDATE table SET number=number-1 WHERE id=1,这类SQL的操作,是非幂等性操作!
非计算式的更新:这类操作是指UPDATE table SET name=“SaltIce” WHERE id=520,这类SQL操作,这种是属于幂等性的操作!
小总结
只有不带唯一索引插入(insert)和计算式的更新(update)会引起幂等性问题的
解决方案
一、单机幂等性
- 通过数据库索引字段的唯一性约束
数据库索引字段可设置唯一性约束,在插入数据的时候,如果索引字段重复,会返回23000 错误码,在web开发时,一般会报异常的,可统一拦截处理,返回请勿重复提交的友好响应。
下面多说一点哈比如说普通的插入数据 员工表里有主键 员工编号
员工编号就是唯一的
这一步唯一性的校验 可放在代码里判断 或者 数据库里做约束。
1.放在代码里判断的情况
- 并发。要自己实现,要控制并发和加锁。
- 索引。唯一性的校验取出的数据没有索引,没有数据库效率高。
- 性能。每次插入更新之前都要查一次数据库,好像也没有减少数据库的性能消耗。还不如让数据库系统自己校验。
2.数据库里做约束
数据库做唯一的一个风险是跟不上业务,想改回代码里处理的时候有点儿蛋疼。
— 就比如说你举的例子就很经典,员工工号似乎是唯一的,甚至能当主键,但可能明天的业务需求就可能变成:离职员工回归后新开账号(原有数据不继承)但工号仍使用原来的工号。
— 如果你仍然坚持用数据库的唯一来处理工号,就得改全部有关联数据的地方增加重新入职的清理逻辑,可是很多数据甚至是不能清理的,一地鸡毛。如果是代码里的逻辑,直接再插一条数据完事儿,原来的唯一检查改成入职状态下工号唯一就行了。数据库唯一约束,分表无法使用
要做严格唯一约束就弄个 redis 之类的内存校验或者从业务上允许重复
- 前端自己携带请求id或者token啥的
通过前端请求携带一个参数在请求头部或者其它地方,这个id可自定义的,最好重复的可能性很低,比如uuid。
这样在后台接口开发时,可写一个注解专门标注该请求是幂等性的,再配合拦截器一起工作。这样在客户端请求这个接口时,后台可以拦截获取到请求头中的那个id。
- 在执行处理前拦截,判断这个请求是否有重复提交,也就是判断一下服务端是否有保存这个id,如果有,判断这个id的状态是否是请求中,是则返回请勿重复请求的响应。如果没有就将这个id保存起来(可直接保存在内存中,或者保存在Redis中(setnx)),赋予一个处理中的状态,比如:请求中。
- 然后执行相应的逻辑处理,在请求处理完成后将这个id删除或者其它操作。当然,如果执行过程中出现异常,首先数据库相关的操作最好是一个事务操作,能够回滚,同时也要做好出现异常后对那个id的状态进行改变,比如:请求失败。具体可自己决定。
- 前端提交前从服务器获取一个token或者id,和上一个有点类似
- 进入填写资料的页面时从服务器获取一个这个提交的token,服务端会报存这个token,保存方式看自己吧,推荐使用redis。
- 填写完资料后客户端发起请求,并携带这个token
- 服务端通过注解与拦截器获取这个token,然后在redis中执行setnx操作,判断是否成功来决定让与不让这个请求。
但是:有人突发奇想:假设有1000个请求,重复请求一般不到10个。为了这不到10个请求的正确性,让剩下990个正常的请求都多一个查询流程,这似乎不大妥吧!
二、分布式幂等性
请移步查看我下一篇文章:https://blog.csdn.net/qq_41257365/article/details/108967795
参考文章:https://mp.weixin.qq.com/s?__biz=MzAxNjk4ODE4OQ==&mid=2247485041&idx=1&sn=f1a3191eda6ed19a18d86e1223c9ec65&chksm=9bed2703ac9aae15fc563f67e5ecfcc428fbe746c909ff5ae24c9b6e7083e81d1d4823e9de15&scene=21#wechat_redirect