最近在本地开发测试的时候,遇到一个表单重复提交的现象。其实缘由很简单,由于网络延迟的问题,我点击了两次提交按钮,数据库里生成了两条记录。其实这种现象之前也有遇到过,通常都是提交后把按钮置灰,没法再次提交,这是很常见的客户端处理的方式。html
可是这真的有从根本上解决问题吗,虽然客户端解决了屡次提交的问题,可是接口中依旧存在着问题。假设咱们不是从客户端提交,而是被其余的系统调用,当遇到网络延迟,系统补偿的时候,是否也会遇到这种问题呢。看了下网上关于这类问题的解决方案,须要实现接口的幂等性。概念很高大上,结合个人实际的理解其实幂等性就是一个操做,不论执行多少次,产生的效果和返回的结果都是同样的。mysql
以个人实际案例来说,就是不管我点击提交按钮多少次,数据库应该只有一条记录才对。可能这个案例还不太符合幂等性的定义,再举个咱们都很切身的案例,当咱们去参加一些电商的抢购活动,假设网络卡顿,这时候不少人确定会屡次点击支付按钮,假设支付接口没有作幂等性校验,这时候会发生什么状况,确定会发生屡次扣款的状况。redis
在编程中.一个幂等操做的特色是其任意屡次执行所产生的影响均与一次执行的影响相同。既然是这样咱们的查询和删除不就是屡次执行的结果和一次执行的相同吗。是的,查询和删除拥有自然的幂等性,固然删除这个第一次执行和后面执行的返回值可能会有所不一样,可是最终的效果是一致的。因此须要咱们额外实现的幂等性接口主要是新增和更新操做。sql
这种方法也是我目前在表单重复提交服务端的解决方案,技术原理很简单。数据库
这种方式分红两个阶段:申请token阶段和执行新增操做。
第一阶段,在进入到新增页面以前,须要服务端发起一次申请token的请求,服务端必定的逻辑得出Token,并将token保存到Redis缓存中并设置生效时间,为第二阶段使用,注意保证token的惟一性。
第二阶段,新增页面拿着申请到的token发起新增请求,服务端执行删除token操做,若是返回0表示Token不存在,为非法请求,若是返回1则为第一次请求,发起新增请求。
注意:redis要用删除操做来判断token,删除成功表明token校验经过,若是用select+delete来校验token,存在并发问题,不建议使用。编程
好比:以上面抢购案例为例,咱们点击支付按钮,向支付系统提交支付申请,这时候支付系统会将一条记录插入到支付状态表,这个表将支付的订单号设为惟一索引,第一次请求将支付订单记录插入表中并设置为未支付同时返回给支付系统完成实际支付操做,后续重复请求就会由于惟一索引致使插入失败而不会再走后续的实际支付操做。就好像一种另类的锁机制。缓存
悲观锁乐观锁的用法,在我这些年的开发中应用仍是比较普遍的。
悲观锁,就是悲观的认为数据会被改变,在数据修改的过程当中始终是加锁的。其余线程不管是读仍是写都没法拿到数据。网络
select * from t_goods where id=1 for update;
与普通查询不同的是,咱们使用了select…for update的方式,这样就经过数据库实现了悲观锁。使用悲观锁的原理就是,当咱们在查询出goods信息后就把当前的数据锁定,直到咱们修改完毕后再解锁。那么在这个过程当中,由于goods被锁定了,就不会出现有第三者来对其进行修改了。并发
乐观锁,相对悲观锁来说更为普遍一些,由于乐观锁不依赖数据库,只会在update的一瞬间加锁,其他处理过程当中并不加锁。分布式
UPDATE t_goods SET STATUS = #{status},name=#{name},version=version+1 WHERE id = #{id} and version=#{version}
乐观锁每次只会有一个线程执行成功,其余线程由于条件发生了改变,而执行失败,这样就避免了数据覆盖的可能性。
乐观锁悲观锁虽然很好的解决了数据不一致的问题,可是也要学会善用。由于数据库的锁机制,条件字段必定要是主键或者惟一索引,否则会形成锁表或者锁无限的可能,能够说印象深入,我就曾由于这个被狠批过。o(╥﹏╥)o
若是有同窗不了解悲观锁和乐观锁的,能够看下这两篇深刻了解下。后面我也会梳理下mysql的锁机制总结分享出来,目前本身对数据库锁机制也是只知其一;不知其二。
能够用redis或zookeeper实现分布式锁。以电商支付为案例,订单发起支付请求,支付系统首先查询订单是否已经支付,若是已经支付,直接返回已支付。若是未支付去Redis缓存中查询是否存在该订单号的Key,若是不存在,则向Redis增长Key为订单号,已存在返回重复操做。再次查询是否完成支付,若是没有则进行支付,支付完成后删除该订单号的Key。经过Redis作到了分布式锁,只有这笔订单支付请求完成,下次请求才能进来。相比惟一索引,将并发放到了缓存中,较为高效。思路相同,同一时间只能完成一次支付请求。
要点:某个长流程处理过程要求不能并发执行,能够在流程执行以前根据某个标志(用户ID+后缀等)获取分布式锁,其余流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放分布式锁。
分布式锁我并无亲手实践过,相关的原理也是从大佬那里偷师来的,后续亲身实践事后会作不按期更新。
站在大佬的肩膀上,才能更快的撬动地球~