上海网站建设服务,教做软件的网站,网站建设网站建设,php网站建设入门教程啥是幂等#xff1f;
客户端对同一API的多次调用应该产生相同的服务器状态。 通俗的说#xff1a;某个行为重复的执行#xff0c;最终获取的结果是相同的#xff0c;不会因为重复执行对系统造成变化。
比如说同步数据的功能#xff0c;重复可以重复同步#xff0c;如果…啥是幂等
客户端对同一API的多次调用应该产生相同的服务器状态。 通俗的说某个行为重复的执行最终获取的结果是相同的不会因为重复执行对系统造成变化。
比如说同步数据的功能重复可以重复同步如果可以就表示是幂等的。
常见场景
HTTP方法 幂等方法GET、PUT、DELETE 非幂等方法POST、PATCH 支付系统重复支付请求不应导致多次扣款 订单创建重复提交订单不应创建多个相同订单
实现幂等性的方法
唯一标识符为每个操作分配唯一ID服务器记录已处理ID 版本号/时间戳资源更新时携带版本信息 状态机确保操作只在特定状态下执行 数据库约束利用唯一索引防止重复数据
具体
1 update where利用数据库的行级锁保证查询和更新是原子性的。伪代码如下。
String rechargeId 充值订单id;// 根据rechargeId去找充值记录如果已处理过则直接返回成功
RechargePO rechargePo select * from t_recharge where id #{rechargeId};// 充值记录已处理过直接返回成功
if(rechargePo.status1){return SUCCESS;
}开启Spring事务// 下面这个sql是重点重点在where后面要加 status 0 这个条件count表示影响行数
int count (update t_recharge set status 1 where id #{rechargeId} and status 0);// count 1表示上面sql执行成功
if(count!1){// 走到这里说明有并发直接抛出异常throw new RuntimeException(系统繁忙请重试)
}else{//给账户加钱update t_account set balance balance #{rechargePo.price} where id #{rechargePo.accountId}
}提交Spring事务2 基于版本号的乐观锁
String rechargeId 充值订单id;// 根据rechargeId去找充值记录如果已处理过则直接返回成功
RechargePO rechargePo select * from t_recharge where id #{rechargeId};// 充值记录已处理过直接返回成功
if(rechargePo.status1){return SUCCESS;
}开启Spring事务// 期望的版本号
Long exceptVersion rechargePo.version;// 下面这个sql是重点重点在set后面要有version version 1where后面要加 status 0 这个条件count表示影响行数
int count (update t_recharge set status 1,version version 1 where id #{rechargeId} and version #{exceptVersion});// count 1表示上面sql执行成功
if(count!1){// 走到这里说明有并发直接抛出异常throw new RuntimeException(系统繁忙请重试)
}else{//给账户加钱update t_account set balance balance #{rechargePo.price} where id #{rechargePo.accountId}
}提交spring事务3 关联一个唯一约束辅助表
-- 幂等辅助表
create table if not exists t_idempotent
(id varchar(50) primary key comment id主键,idempotent_key varchar(200) not null comment 需要确保幂等的key,unique key uq_idempotent_key (idempotent_key)
) comment 幂等辅助表;重点关注第二个字段idempotent_key这个字段添加了唯一约束说明同时向这个表中插入同样值的idempotent_key则只有一条记录会执行成功其他的请求会报异常而失败让事务回滚。
String rechargeId 充值订单id;// 根据rechargeId去找充值记录如果已处理过则直接返回成功
RechargePO rechargePo select * from t_recharge where id #{rechargeId};// 充值记录已处理过直接返回成功
if(rechargePo.status1){return SUCCESS;
}// 生成idempotentKey这里可以使用业务id:业务类型那么我们这里可以使用rechargeId:RECHARGE_CALLBACK
String idempotentKey rechargeId:RECHARGE_CALLBACK;// 幂等表是否存在记录如果存在说明处理过直接返回成功
IdempotentPO idempotentPO select * from t_idempotent where idempotent_key #{idempotentKey};
if(idempotentPO!null){return SUCCESS;
}开启Spring事务(这里千万不要漏掉一定要有事务)// count表示影响行数这个sql比较特别看起来并发会出现问题实际上配合唯一约束辅助表就不会有问题了
int count update t_recharge set status 1 where id #{rechargeId};// count ! 1表示未成功
if(count!1){// 走到这里直接抛出异常让事务回滚throw new RuntimeException(系统繁忙请重试)
}else{//给账户加钱update t_account set balance balance #{rechargePo.price} where id #{rechargePo.accountId}
}String idempotentId ;
// 这里是关键一步向 t_recharge 插入记录如果有并发过来只会有一个成功其他的会报异常导致事务回滚上面的
insert into t_recharge (id, idempotent_key) values (#{idempotentId}, #{idempotentKey});提交spring事务