概念:
在web中的:(下面描述讲解的是web)
幂等:
对于同一种行为,如果执行不论多少次,最终的结果都是一致相同的,就称这种行为是幂等的。
(个人理解:不管是一次,还是多次操作,我们返回同样的结果,且不修改状态信息,接口可重复调用)
非幂等:
对于同一种行为,如果最终的结果与执行的次数有关,每次执行后结果都不相同,就称这种行为为非幂等。譬如:累加在数学上的概念:
这是个高等代数中的概念。简而言之就是x^Y=x x可能是任何元素,包括(数、矩阵等)
背景:
- 前端重复提交选中的数据,应该后台只产生对应这个数据的一个反应结果;
- 我们发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统bug重发,也应该只扣一次钱;
- 发送消息,也应该只发一次,同样的短信发给用户,用户会哭的;
- 创建业务订单,一次业务请求只能创建一个,创建多个就会出大问题等等很多重要的情况都需要幂等的特性来支持。
技术的实现:
-
可以通过http协议(超文本传输协议):来区分。get,put,delete是天然的幂等操作,所以在尽量使用这些方法。
-
建立唯一索引,能防止新增的脏数据,比如在银行的账户中,每个支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建银行账户多个,那么给账户表中的用户ID加唯一索引,所以一个用户新增成功一个资金账户记录。要点:唯一索引或唯一组合索引来防止新增数据存在脏数据(当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可)。
-
token + (redis) 的机制(token的特点 : 一次有效性,时效性,可以对流量进行控制),可以防止页面的重复提交。具体实现 : 发生的原因由于重复的点击 , 网络原因导致了多次发送请求,或者由于nginx重发等情况会导致数据被重复的提交 ; 解决的方案 :
在数据提交给后台服务器处理时,要向服务器申请token(全局唯一的变量),将token存放到redis(可以是其他的)中,设置token的有效时间。
当服务器接受到请求进行处理时,对token进行校验,校验通过后并删除该token,并生成新的token返回 -
悲观锁和乐观锁:
悲观锁 : 更新数据时认为此次操作会造成数据的缺失。
注意 : id字段必须时主键或者唯一索引,不然的话就锁了整个表,导致效率降低,必须开启事务,缺点容易造成死锁,不建议使用
乐观锁 : 更新数据时认为此次操作不会造成数据缺失,所以只是在更新数据那一刻锁表,其他时间不锁表,相对于悲观锁的效率更高。
实现方式 : 在数据库的表中增加一个字段version(版本号)或者是其他的状态位。在对表进行操作时同时取出数据和标志位version,当要修改数据表时,对version进行判断是否与之前取出的一致,如果一致则修改,如果不一致则不修改。同时version自增一存入数据库。
(注意 : 注意:乐观锁的更新操作,最好用主键或者唯一索引来更新,这样是行锁,否则更新时会锁表)
- 分布式锁,加入系统部署在分布式系统上,构建全局的唯一索引比较困难(唯一的字段无法没法确定),这时候可以引入分布式锁,通过第三方的系统(redis或zookeeper),在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路,引入多多个系统,也就是分布式系统中得解决思路
要点:某个长流程处理过程要求不能并发执行,可以在流程执行之前根据某个标志(用户ID+后缀等)获取分布式锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放分布式锁(分布式锁要第三方系统提供);
- 状态机制,在设计单据相关的业务,或者是任务相关的业务,肯定会涉及到状态机(状态变更图),就是业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。
总结:
幂等与你是不是分布式高并发还有JavaEE都没有关系。关键是你的操作是不是幂等的。一个幂等的操作典型如:把编号为5的记录的A字段设置为0这种操作不管执行多少次都是幂等的。一个非幂等的操作典型如:把编号为5的记录的A字段增加1这种操作显然就不是幂等的。要做到幂等性,从接口设计上来说不设计任何非幂等的操作即可。譬如说需求是:当用户点击赞同时,将答案的赞同数量+1。改为:当用户点击赞同时,确保答案赞同表中存在一条记录,用户、答案。赞同数量由答案赞同表统计出来。总之幂等性应该是合格程序员的一个基因,在设计系统时,是首要考虑的问题,尤其是在像支付宝,银行,互联网金融公司等涉及的都是钱的系统,既要高效,数据也要准确,所以不能出现多扣款,多打款等问题,这样会很难处理,用户体验也不好。