前端重复提交选中的数据,应该后台只产生对应这个数据的一个反应结果。我们发起一笔付款请求,应该只扣用户账户一次钱,当遇网络重发或系统bug重发,也应该只扣一次钱;发送消息,也应该只发一次,同样的短信发给用户,用户会哭的;创建业务订单,一次业务请求只能创建一个,创建多个就会出大问题。等等很多重要的情况,这些逻辑都需要幂等的特性来支持
二、幂等性概念
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“getUsername()和setTrue()”函数就是一个幂等函数.更复杂的操作幂等保证是利用唯一交易号(流水号)实现.我的理解:幂等就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的。
三、技术方案
1、查询操作
查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作
2、删除操作
删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个)
3、唯一索引
防止新增脏数据 比如:支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建资金账户多个,那么给资金账户表中的用户ID加唯一索引,所以一个用户新增成功一个资金账户记录
要点: 唯一索引或唯一组合索引来防止新增数据存在脏数据 (当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可)
4、token机制,防止页面重复提交
业务要求:
页面的数据只能被点击提交一次
发生原因:由于重复点击或者网络重发,或者nginx重发等情况会导致数据被重复提交
解决办法:集群环境:采用token加redis(redis单线程的,处理需要排队) 单JVM环境:采用token加redis或token加jvm内存
处理流程:
数据提交前要向服务的申请token,token放到redis或jvm内存,token有效时间
提交后后台校验token,同时删除token,生成新的token返回
token特点:
要申请,一次有效性,可以限流
注意:redis要用删除操作来判断token,删除成功代表token校验通过,如果用select+delete来校验token,存在并发问题,不建议使用
5、悲观锁 获取数据的时候加锁获取
select * from table_xxx where id='xxx' for update;
注意:id字段一定是主键或者唯一索引,不然是锁表,会死人的悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用
6、乐观锁
乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。
update table_xxx set name=#name#,version=version+1 where version=#version#
update tablexxx set avaiamount=avaiamount-#subAmount# where avaiamount-#subAmount# >= 0
update tablexxx set name=#name#,version=version+1 where id=#id# and version=#version#
update tablexxx set avaiamount=avaiamount-#subAmount# where id=#id# and avai_amount-#subAmount# >= 0
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) | 黑马程序员IT技术论坛 X3.2 |