1.1 幂等性的概念
Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.
- 多次请求一个资源时,应该有相同的结果(网络超时等问题除外)。 也就是说,一次请求被多次重复执行对资源自身的影响与第一次执行的影响相同。
- 例如:用户查询结果多次扣款。 流水记录变成两个就是没有保证接口的幂等性。
1.2 防重设计和幂等设计
- 防重设计和幂等设计在很多情况下是可以通用的,解决方案也比较类似,区别在于:
- 防重设计:避免产生重复数据,对返回结果没有限制。
- 幂等设计:避免产生重复数据,要求每次请求都返回同样的结果。
1.3 常见场景
- FORM 表单,按钮重复点击,产生 ID 不一样的两条重复数据。
- 浏览器页面返回按钮,此时数据依然存在,重复提交。
- 接口重试机制(超时问题),重试过程中会产生重复数据。
- 微服务调用,网络导致请求失败,feign 触发重复机制。
- MQ 同一条消息重复读取。
1.4 汇总分析(数据库的角度)
- select 语句在数据不变的情况下,多次查询的结果相同,天然幂等操作。
- insert 语句重复提交的情况下,会产生数据的重复。
- delete 语句一次或者多次结果都是删除数据(已删除的数据不存在,返回0。 已删除的数据有多个,结果有多个。 删除操作也有乘方等属性)。
- update 语句在大多数场景中的结果相同,但增量修改需要乘方等保证。例如表中存在 version 字段,此时更新时不是幂等。
1.5 保障幂等方式
1.5.1 唯一性索引
alter table xxx add UNIQUE KEY (key);
- 异常精准捕获,以便返回数据。
- DuplicateKeyException 异常。
- MySQLIntegrityConstraintViolationException 异常。
1.5.2 单独表:防重复
- 核心:防重表和业务表必须在同一个事务中。
- 和唯一性索引的主要区别在于,并非所有的场景都不允许产生重复的数据。例如逻辑删除。
1.5.3 status 机制
- 根据影响行数来判断是否更新成功。
update table set status = 1 where id = 1 and status=2;
1.5.4 数据库悲观锁
select * from table id = 1;
select * from table id = 1 for update;
- 首先执行一遍查询,如果此时不满足条件,就直接返回了,无需锁住单条数据。
- 悲观锁之后,再进行逻辑判断。
- 执行业务操作。
- 备注:mysql 存储引擎选用 innodb ,悲观锁字段最好是主键或唯一索引,不然可能会锁表。
1.5.5 数据库乐观锁(version )
select id,version,amount from table id = 1;
-- version 查询结果为 0
update table set amount = amount + 1,version = version + 1
where id=1 and version = 0;
- 根据sql的执行结果影响行数判断是否执行成功。
1.5.6 token机制
- 生成全局唯一的 token,token 放到 redis 中(注意设置过期时间),页面跳转时获取 token 。
- 请求时携带token ,执行提交逻辑。
1.5.7 分布式锁
- 通过 setNx、set 命令或者 Redission 第三方的框架。
1.6 小结
- 幂等不仅对一次(或多次)请求没有副作用,对资源也没有副作用。 例如,select 对数据库不会产生任何影响,因为没有被insert、update、delete。
- 网络超时等问题不在幂等讨论范围内。
- 幂等性是系统服务对外约定而不是实现的,约定如果接口调用成功,则外部多次调用对系统的影响是一致的。 声明为幂等的服务,认为外部调用失败是常态,失败后一定会有重试。
更多精彩欢迎关注微信公众号《格子衫007》!