前言
我们考虑要不要做幂等的位置一般是MQ消费者、RPC、HTTP接口。消费者消费消息可以看做源源不断的请求调用消费者接口。因此本文将幂等位置统称为接口,探讨接口如何提供幂等能力。
一、概念
1. 什么是
一次和多次执行的结果相同。
但并非所有业务场景都需要保证幂等性。因为保证幂等性需要额外的开销
天然幂等场景:比如查询、删除、更新
不幂等场景:插入、自增/减(update xxx set a = a+1)
如果一个接口涉及到了不幂等场景,那么就要考虑做幂等了。
2. 为什么
服务调用方或者某些中间件因为网络等异常重复调用接口,如果接口没有幂等能力会产生异常数据。
3. 接口幂等的能力
幂等判定
幂等的判定需要一个能够判断是否重复的唯一标识。如果判定重复则不再处理请求。
这个标识可以是接口参数的一个字段(类似表单主键),也可以是接口参数的多个字段按约定生成的唯一标识(类似表多主键)
两种标识方式各有优劣。
多字段一般用业务信息组合成唯一标识。优点是调用方不用考虑生成幂等ID,缺点是对字段的选择需要谨慎,并且对于一些不想要幂等的场景还要增加控制接口是否幂等的字段(脚本批量调用接口添加人)
// 接口:添加一个人,两个字段组合成唯一标识
POST /addPerson
{
"name":"",
"time":""
}
单字段作为幂等标识一般与业务解耦,优点是灵活性比较高,不想幂等可以约定不传幂等字段。缺点是调用方成本增加,需要考虑幂等ID的生成,并且存在多调用方时可能有幂等ID的冲突。
// 接口:添加一个人,id作为幂等ID
POST /addPerson
{
"id": "",
"name":"",
"time":""
}
幂等时效
有了用于幂等判定的唯一标识后,还可以规定这个标识的有效期。比如1s内标识重复则不再处理请求,也可以永久有效。跟实现方式有关,永久有效一般用数据库实现,短期有效一般用带过期时间的缓存实现。
短期有效一般用于幂等ID不需要长期存储的场景,也能缓解多方调用幂等ID冲突的问题。
二、各种场景的实现方式
- 防重表,单独一张表,主键是幂等ID
- token
- Redis防重
- 业务表主键是幂等ID
- 业务表幂等ID列设置了唯一索引
- 自更新:乐观锁,版本号方式
- 自更新:悲观锁