[TOC]
## 一、方法级幂等性
* 主要作用于防止重复提交
* service实现类继承`SuperServiceImpl`,例子如下:
~~~
//例子1
private final static String LOCK_KEY_USERNAME = CommonConstant.LOCK_KEY_PREFIX+"username:";
String username = sysUser.getUsername();
boolean result = super.saveIdempotency(sysUser, lock
, LOCK_KEY_USERNAME+username
, new QueryWrapper<SysUser>().eq("username", username));
//例子2
private final static String LOCK_KEY_CLIENTID = CommonConstant.LOCK_KEY_PREFIX+"clientId:";
String clientId = client.getClientId();
boolean result = super.saveOrUpdateIdempotency(client, lock
, LOCK_KEY_CLIENTID+clientId
, new QueryWrapper<Client>().eq("client_id", clientId)
, clientId + "已存在");
~~~
## 二、幂等性的定义
### 2.1.什么是幂等性
HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。
简单来说,是指无论调用多少次都不会有不同结果的 HTTP 方法。
### 2.2.什么情况下需要幂等
就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。举个最简单的例子,那就是支付,用户购买商品使用支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条。
## 三、确定需要幂等性的范围
### 3.1. 确定大范围
1. 请求层面:`读请求`、`写请求`
* 其中读请求没有影响数据变化不需要做幂等性
2. 微服务层面:`负载均衡`、`api网关`、`业务逻辑层`、`数据访问层`
* 其中负载均衡、api网关、业务逻辑层没有影响数据变化不需要做幂等性
> 总结:上面的范围里只有`数据访问层`和`写请求`
### 3.2. 数据访问层-写请求
1. Insert
* **需要做幂等性**
2. Update
* 直接更新某个值的:不需要做幂等性
* 累加操作等计算式的更新:**需要做幂等性**
3. Delete
* 重复删除结果是一样:不需要做幂等性
## 四、幂等性解决方案
* 没有最优的方案只有最适合的,因为这个和业务的逻辑强相关,所以就简单列举通用的方案
### 4.1. Insert幂等方案
1. **数据字段增加唯一索引**
* 优点:实现简单方便
* 缺点:影响数据库性能不适合该字段会被频繁更新的场景,唯一索引比普通索引在写操作上开销会大很多
2. **insert时使用临时表查询判断**
~~~
insert into sys_user(name,password)
select 'admin', '123456' from dual
where not exists(select 1 from sys_user where name='admin');
~~~
* 优点:不需要创建唯一索引,语法相对通用(mysql和oracle)
* 缺点:写操作会增加一次select子查询开销,增加sql语法的复杂度可读性较差
3. **细粒度分布式锁+select + insert**
* 意思就是先加一个细粒度的分布式锁,然后select查一下是否存在,不存在再insert
* 例子可参考文档最上面的`一、方法级幂等性`例子
* 优点:性能影响较少,使用的是细粒度锁,所以只有重复提交记录时才会阻塞
* 缺点:写操作会增加一次select开销,实现难度相对较大因为需要分布式细粒度锁
### 4.2. Update计算操作幂等方案
* 这个需要结合具体业务来设计方案,常用的场景可通过版本号的方式来控制
* 在表里面添加`version`字段
~~~
alter table sys_user add version int default 0;
~~~
* 然后更新的时候通过这个`version`来判断是否为过期无效操作,这是乐观锁的一种思路
~~~
update sys_user set age=age+1, version=version+1
where version=xx
~~~
- 微服务介绍
- 软件架构设计
- 系统简介
- 运行环境
- 模块介绍
- 启动部署命令参数
- 打包说明
- 开发说明
- Java8
- 认证理论
- 有网络隔离
- 无网络隔离
- token自动续签设计
- url级权限控制
- 单点登录
- 登录
- 用户名密码(+验证码)登录
- 通过openId获取token
- 通过手机号获取token
- 第三方系统接口对接
- 第三方系统单点登录
- 通用刷新token
- 账号登出接口
- 统一异常处理
- 日志埋点工具
- 审计日志
- yml自定义配置自动提示
- Redis使用
- CacheManager集成
- 搜索中心
- 网关zuul动态路由
- swagger
- 解决开发环境的服务冲突和实例乱窜
- 多租户理论
- 多租户实现
- 分布式锁
- 分布式id生成器
- 分布式事务
- 分库分表sharding-sphere
- 消息队列
- 系统幂等性
- X 实时搜索系统设计
- Spring Cloud性能调优
- 链路跟踪
- JWT的RSA非对称密钥生成
- jdk
- Oracle
- OpenJDK
- Dragonwell
- JVM介绍
- 常见JVM内存错误及解决方案
- JVM分析工具详解
- nexus
- docker
- 安装
- docker-compose安装
- 私有仓库搭建
- 指定数据储存目录
- 添加私有仓库
- 提交拉取镜像
- X 免Dockerfile文件构建项目镜像
- 持续集成部署CI/CD
- Confluence知识管理
- JIRA事务与项目跟踪软件
- Gitlab代码仓库
- Jenkins自动化部署
- SonarQube代码检测
- Rancher容器管理平台
- nacos
- 部署
- Rancher部署nacos
- 注册中心
- 配置中心
- 多环境
- 生产部署方案
- X 通过Nginx来实现环境隔离
- Sentinel详解
- 动态规则扩展
- 在生产环境中使用 Sentinel
- 启动配置项
- X 网关流控
- ELK日志系统和慢查询SQL
- docker安装
- 普通安装
- Filebeat安装部署
- ES数据定期删除
- 没数据问题排查思路
- X Logstash的grok语法调试
- 慢查询sql
- 审计日志
- Elasticsearch性能优化
- APM监控
- SkyWalking介绍
- Zipkin,Pinpoint,SkyWalking三种服务链路监控组件分析
- 服务端部署
- 客户端部署
- 日志清理
- Metrics监控
- 二进制包部署
- docker部署
- docker安装各组件监控
- Grafana仪表盘和告警配置
- JMeter压力测试工具
- 使用说明
- 分布式事务
- TX-LCN(同步)
- X SEATA(同步)
- X RocketMQ(异步)
- 消息队列
- RocketMQ
- RocketMQ安装部署
- RocketMQ常见异常处理
- SpringCloud-Stream
- RabbitMQ
- Redis
- 单机安装
- 主从复制
- 主从复制+哨兵
- cluster集群
- 持久化方案
- MySQL
- 单机安装
- 主从复制
- 主从切换
- 主主复制
- 高可用
- 分表分库
- Canal数据库日志解析消费
- Canal安装
- 实时同步数据到ElasticSearch
- FastDFS
- docker安装
- centos安装
- 项目管理系统
- 禅道
- 111