幂等性的解释及解决办法

  1. 什么是幂等性
    执行一次和执行多次的结果是一致的.
    对于接口幂等性而言,就是用户对于同一操作发起的一次请求和多次请求的结果是一致的,不会因为多次点击而产生了副作用.比如支付场景中,用户购买了商品支付扣款成功,但是返回结果时网络异常,此时扣款成功,用户再次点击按钮时再次扣款返回结果成功,有两条支付流水,这就没有保证接口的幂等性.

  2. 什么情况下需要防止没有保证幂等性的情况

  • 用户多次点击按钮
  • 用户页面回退再提交
  • 微服务互相调用,由于网络问题,导致请求失败.feign触发重试机制
  1. 幂等解决方案之token令牌机制
  • token令牌机制
    • 1.服务端提供了发送token的接口.我们在分析业务的时候,那些业务是存在幂等问题的,就必须在执行业务前先去获取token,服务器会把token保存到redis中
    • 2.然后调用业务请求接口,把token带过去,一般放在请求头部
    • 3.服务器判断token是否存在于redis中,存在表示第一次请求,然后删除token,继续执行业务
    • 4.如果判断token不存在于redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码不会被重复执行.
  • token令牌机制解决方案危险性
    • 先删除token还是后删除token
      如果先删除token,业务可能因为宕机等原因没有执行,而token已经删除,即使重试该请求也无法执行
      如果后删除token,可能业务执行成功,但是在删除token之前由于宕机等原因没能删除成功,该请求还可以继续执行,导致业务执行多次.
      解决办法: 先删除token,如果业务调用失败,则重新获取token再次请求
    • token获取、比较、删除必须是原子性的
      如果不能保证token的获取、比较、删除是原子性的,可能导致高并发下都获取到相同的数据并且判断成功,执行多次业务
      解决办法: redis中使用lua脚本实现token的获取、比较、删除是原子操作
  1. 幂等解决方案之锁机制
  • 数据库悲观锁
    select * from xxx where id = 1 for update (给id为1的行加上行锁)
    (扩展: 共享锁可以使用select … lock in share mode)
    悲观锁使用时一般伴随事务一起使用,数据锁定的时间可能会很长,需要根据实际情况选用.另外id字段一定是主键或者唯一索引,否则可能会造成锁表的结果.
  • 数据库乐观锁
    update table_name set count = count + 1, version = version + 1 where id = 1 and version = 1使用版本号进行控制,每次更新版本号+1,从而保证了幂等性
  • 业务层分布式锁
    如果多个机器可能在同一时间处理相同的数据,比如多台机器定时任务都拿到了相同的数据处理,我们就可以加分布式锁,锁定此数据,处理完成后释放锁.获取到锁的必须先判断这个数据是否被处理过.
    分布式锁核心: 保证加锁(占位+过期时间)和删除锁(判断+删除)的原子性,其中锁的value值是不同的(比如UUID).
    分布式锁参考链接
  1. 幂等解决方案之唯一约束
  • 数据库唯一约束
    插入数据应该按照唯一约束进行插入,比如订单号,相同订单就不可能有两条记录插入.
    如果在分库分表的场景下,路由规则要保证在形同请求下,落地在同一个数据库和同一个表中要不然数据库主键约束就起不到效果了,因为不同的数据库和表主键不相关.
  • redis set防重
    很多数据需要处理,只能被处理一次,比如通过计算数据的MD5将其放入redis的set,每次处理数据,先看这个MD5是否已经存在,存在就不处理.
  1. 幂等解决方案之防重表
    比如在订单系统中,使用订单号作为去重表的唯一索引,把唯一索引插入去重表,再进行业务操作,且他们在同一个事务中.这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,从而避免了幂等问题.这里要注意的是,去重表和业务表应该在同一库中,这样就保证了同一个事务,即使业务操作失败了,也会把去重表的数据回滚,这个很好的保证了数据一致性.

  2. 幂等解决方案之全局请求唯一id
    调用接口时,生成一个唯一id,redis将数据保存到set集合中(去重),存在即处理过,可以使用nginx设置每一个请求的唯一id.
    proxy_set_header X-Request-Id $request_id;
    比如应用于feign远程调用,被调用方如果已经处理过相同id的请求,则不再处理.设置唯一id也可以实现链路追踪.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值