都2021年了,你还不懂幂等性问题的解决方案?

     hello,大家好,很抱歉昨天没有发推文,因为昨天在学习自媒体运营的知识,耽搁了,不过今天给大家补上了图片图片图片

     记得上次领导找我谈话,你懂的,先唠嗑唠嗑XXX,  然后顺便考察了我一个问题,幂等性问题是如何解决的?    我一想,这种问题不是面试宝典里经常有的吗,于是小慕果不其然的答出来了7种解决思路啦,接下来小慕就给大家分享一下我是怎么回答领导的提问的图片图片图片

 

方案一:前端做校验

 在用户点击完提交按钮后,我们可以把按钮设置为不可用或者隐藏状态。  前端限制比较简单,但有个致命错误,如果碰到懂行的用户通过模拟网页请求来重复提交请求,绕过了前端限制,那就不行了。

 

方案二:数据库唯一索引

防止订单多次插入的最简单直接方法就是创建唯一索引,然后插入的时候可能语句有细微的不同。但目的都是保证相同记录在数据库中只存在一条。

方法一:给数据库添加唯一索引,然后如果执行时捕捉到了DuplicateKeyException会明白是重复插入导致的,继续往下执行业务即可。(注:捕捉到了异常就在catch里做update数据的操作)

方法二利用MySQL自带的关键字ON DUPLICATE KEY UPDATE 实现不存在则插入,存在则更新的操作,该关键字不会删除原有的记录。(注:这种方式不管是insert还是update都会让主键id自增,从而可能很快耗尽id自增资源,所以慎用)

方法三:replace into 主要作用类似 INSERT 插入操作,replace into底层是先删除后插入数据,会破坏索引、重新维护索引。需注意必须要有主键或唯一索引才能有效,否则replace into就只新增了。

 

方案三:去重表

去重表的机制是根据mysql唯一索引的特性来的,大致流程:

1、客户端先请求服务端,服务端先将这次的请求信息存入一张mysql的去重表中,这张表要根据这次请求的其中某个特殊字段建立唯一索引,或者主键索引。

2、判断是否插入成功,如果插入成功,则继续做后续业务请求。如果插入失败,则代表已经执行过当前请求。

图片

 

方案四:悲观锁

方式一:如果是在单节点,不跨JVM,那就使用Java的synchronize或lock加锁,这样一来就不会有并发线程造成幂等性问题

 

方式二:如果是分布式系统,可以使用redis来实现分布式锁,使用Redis中的setnx操作,将幂等性的保证屏障设置在分布式锁中。如果setnx成功了说明这是第一次进行数据插入,继续执行SQL语句即可。如果setnx失败了,那说明已经执行过了。例如每次写请求之前都是服务端返回一个唯一编号给客户端,客户端带着这个请求号做请求,服务端即可完成去重拦截。

 

方式三:MySQL自带的for update,来实现串行化 。 

1、当线程A执行for update,数据会对当前记录加锁,其他线程执行到此行代码的时候,会等待线程A释放锁之后,才可以获取锁,继续后续操作。

 

2、事务提交时,for update获取的锁会自动释放。

注:该模式的缺点是,如果业务处理比较耗时,并发情况下,后面线程会长期处于等待状态,占用了很多线程,让这些线程处于无效等待状态,而web服务中的线程数量一般有限的,如果大量线程由于获取for update锁处于等待状态,不利于系统并发操作。

 

方案五:乐观锁

update xxx  set account = account + 10,version = version + 1

 

方案六:token令牌

这种方式分成两个阶段:申请token阶段和支付阶段。

第一阶段:在进入到提交订单页面之前,需要订单系统根据用户信息向支付系统发起一次申请token的请求,支付系统将token保存到Redis缓存中,为第二阶段支付使用。

 

第二阶段:订单系统拿着申请到的token发起支付请求,支付系统会检查Redis中是否存在该token,如果存在,表示第一次发起支付请求,删除缓存中token后开始支付逻辑处理;如果缓存中不存在,表示非法请求。

 

实际上这里的token可以认为是一个信物,支付系统根据token确认插入的唯一性。token模式不足之处在于,需要系统间交互两次,流程较上述方法复杂。

 

方案七:唯一请求编号参数

      我们把请求参数(JSON)按KEY做升序排序,排序后拼成一个字符串,作为 KEY 值,但这可能非常的长,所以我们可以考虑对这个字符串求一个 MD5 加密作为参数,以这个参数去取代 reqParam 的位置。

String key = "User="+userId + "Method=" + method + "Param=" + reqParamMD5;

这个key就是唯一的,独一无二的。  

 

但是某些请求用户短时间内重复的点击了(例如 1000 毫秒发送了三次请求),但绕过了上面的去重判断(不同的 KEY 值),我们该怎么解决呢?

那就是在请求参数用MD5加密之前,先剔除时间字段即可。

//虽然两个请求一样,但是请求时间差一秒String req = "{\n" + "\"requestTime\" :\"20190101120001\",\n" + "\"userName\" :\"xiaoming\",\n" + "\"Number\" :\"001\"\n" +  "}";    String req2 = "{\n" +  "\"requestTime\" :\"20190101120002\",\n"  + "\"userName\" :\"xiaoming\",\n"  + "\"Number\" :\"001\"\n" + "}";

我们在加密的时候把requestTime剔除即可。

好,今天的内容就分享到这里了,你们一定要变优秀哦,我们下期再见图片图片图片

 

半山腰总是最挤的,你得去山顶上瞅瞅,拜拜

好,今天的内容就分享到这里了,你们一定要变优秀哦,我们下期再见。

现在公众号迁移到这个啦, 不要迷路了,慕仔们,加油哦!

       接下来的一段时间,我会专注Java技术栈,计算机网络,数据结构和算法,操作系统,设计模式,计算机组成原理,数据库原理,设计模式来做分享,欢迎你们和我一起学习,一起提高,Fighting!
 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java全栈研发大联盟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值