- 浏览: 1133437 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (138)
- JAVA基础 (22)
- Spring (6)
- 设计模式 (2)
- JDK源码 (3)
- java-功能组件 (4)
- 游戏项目 (2)
- linux (13)
- Oracle (2)
- struts (1)
- 字符集 (8)
- HTTP协议 (2)
- java-网络通信 (1)
- 工具软件推荐 (2)
- tomcat (1)
- java-容器框架 (2)
- java-IO框架 (2)
- java-多线程框架 (4)
- java-NIO框架 (0)
- jquery (2)
- 工具使用 (12)
- 加密解密 (1)
- redis (2)
- maven (2)
- svn (1)
- eclipse (1)
- mysql (11)
- 我的收藏 (1)
- JAVA进阶 (26)
- 运维 (3)
- protocol buffer (1)
- 优秀博主 (1)
- nginx (1)
- 算法 (2)
- 故障排查 (4)
- 粤语歌曲 (6)
- 生活总结 (6)
- 高并发 (4)
- 语言训练 (1)
- 读书笔记 (5)
- 诗歌 (1)
- tomcat源码学习 (1)
- 软件词汇 (1)
- git (1)
最新评论
-
ryuhi:
一个是来源source,一个是来源方序列号seq这两个数据要怎 ...
高并发的核心技术-幂等的实现方案 -
xuezhongyu01:
无量 写道Master-Gao 写道理论感觉还行,可以代码我还 ...
高并发的核心技术-幂等的实现方案 -
无量:
Master-Gao 写道理论感觉还行,可以代码我还是不会写。 ...
高并发的核心技术-幂等的实现方案 -
phil_jing:
@RequestParam 默认 true
SpringMVC注解@RequestParam全面解析 -
aguai0:
aguai0 写道第五条里的如果要获取任务执行结果,用Comp ...
JAVA进阶----ThreadPoolExecutor机制
高并发的核心技术-幂等的实现方案
一、背景
我们实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果。
例如:
1. 前端重复提交选中的数据,应该后台只产生对应这个数据的一个反应结果。
2. 我们发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统bug重发,也应该只扣一次钱;
3. 发送消息,也应该只发一次,同样的短信发给用户,用户会哭的;
4. 创建业务订单,一次业务请求只能创建一个,创建多个就会出大问题。
等等很多重要的情况,这些逻辑都需要幂等的特性来支持。
二、幂等性概念
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。
在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“getUsername()和setTrue()”函数就是一个幂等函数.
更复杂的操作幂等保证是利用唯一交易号(流水号)实现.
我的理解:幂等就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的
三、技术方案
1. 查询操作
查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作
2. 删除操作
删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个)
3.唯一索引,防止新增脏数据
比如:支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建资金账户多个,那么给资金账户表中的用户ID加唯一索引,所以一个用户新增成功一个资金账户记录
要点:
唯一索引或唯一组合索引来防止新增数据存在脏数据
(当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可)
4. token机制,防止页面重复提交
业务要求:
页面的数据只能被点击提交一次
发生原因:
由于重复点击或者网络重发,或者nginx重发等情况会导致数据被重复提交
解决办法:
集群环境:采用token加redis(redis单线程的,处理需要排队)
单JVM环境:采用token加redis或token加jvm内存
处理流程:
1. 数据提交前要向服务的申请token,token放到redis或jvm内存,token有效时间
2. 提交后后台校验token,同时删除token,生成新的token返回
token特点:
要申请,一次有效性,可以限流
注意:redis要用删除操作来判断token,删除成功代表token校验通过,如果用select+delete来校验token,存在并发问题,不建议使用
5. 悲观锁
获取数据的时候加锁获取
select * from table_xxx where id='xxx' for update;
注意:id字段一定是主键或者唯一索引,不然是锁表,会死人的
悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用
6. 乐观锁
乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。
乐观锁的实现方式多种多样可以通过version或者其他状态条件:
1. 通过版本号实现
update table_xxx set name=#name#,version=version+1 where version=#version#
如下图(来自网上):
2. 通过条件限制
update table_xxx set avai_amount=avai_amount-#subAmount# where avai_amount-#subAmount# >= 0
要求:quality-#subQuality# >= ,这个情景适合不用版本号,只更新是做数据安全校验,适合库存模型,扣份额和回滚份额,性能更高
注意:乐观锁的更新操作,最好用主键或者唯一索引来更新,这样是行锁,否则更新时会锁表,上面两个sql改成下面的两个更好
update table_xxx set name=#name#,version=version+1 where id=#id# and version=#version#
update table_xxx set avai_amount=avai_amount-#subAmount# where id=#id# and avai_amount-#subAmount# >= 0
7. 分布式锁
还是拿插入数据的例子,如果是分布是系统,构建全局唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统(redis或zookeeper),在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路,引入多多个系统,也就是分布式系统中得解决思路。
要点:某个长流程处理过程要求不能并发执行,可以在流程执行之前根据某个标志(用户ID+后缀等)获取分布式锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放分布式锁(分布式锁要第三方系统提供)
8. select + insert
并发不高的后台系统,或者一些任务JOB,为了支持幂等,支持重复执行,简单的处理方法是,先查询下一些关键数据,判断是否已经执行过,在进行业务处理,就可以了
注意:核心高并发流程不要用这种方法
9. 状态机幂等
在设计单据相关的业务,或者是任务相关的业务,肯定会涉及到状态机(状态变更图),就是业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。
注意:订单等单据类业务,存在很长的状态流转,一定要深刻理解状态机,对业务系统设计能力提高有很大帮助
10. 对外提供接口的api如何保证幂等
如银联提供的付款接口:需要接入商户提交付款请求时附带:source来源,seq序列号
source+seq在数据库里面做唯一索引,防止多次付款,(并发时,只能处理一个请求)
重点:
对外提供接口为了支持幂等调用,接口有两个字段必须传,一个是来源source,一个是来源方序列号seq,这个两个字段在提供方系统里面做联合唯一索引,这样当第三方调用时,先在本方系统里面查询一下,是否已经处理过,返回相应处理结果;没有处理过,进行相应处理,返回结果。注意,为了幂等友好,一定要先查询一下,是否处理过该笔业务,不查询直接插入业务系统,会报错,但实际已经处理了。
总结:
幂等性应该是合格程序员的一个基因,在设计系统时,是首要考虑的问题,尤其是在像支付宝,银行,互联网金融公司等涉及的都是钱的系统,既要高效,数据也要准确,所以不能出现多扣款,多打款等问题,这样会很难处理,用户体验也不好
欢迎大家关注我的公众号:
写的很清楚了,大致代码逻辑已经有了,自己还写不出来啊,我贴一堆代码在博客里也不太好哦,大家对读代码兴趣不大
对抄代码兴趣蛮大的,ctrl+c ctrl+v嘛,哈哈~
写的很清楚了,大致代码逻辑已经有了,自己还写不出来啊,我贴一堆代码在博客里也不太好哦,大家对读代码兴趣不大
是的,程序设计的潜在法律
一、背景
我们实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果。
例如:
1. 前端重复提交选中的数据,应该后台只产生对应这个数据的一个反应结果。
2. 我们发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统bug重发,也应该只扣一次钱;
3. 发送消息,也应该只发一次,同样的短信发给用户,用户会哭的;
4. 创建业务订单,一次业务请求只能创建一个,创建多个就会出大问题。
等等很多重要的情况,这些逻辑都需要幂等的特性来支持。
二、幂等性概念
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。
在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“getUsername()和setTrue()”函数就是一个幂等函数.
更复杂的操作幂等保证是利用唯一交易号(流水号)实现.
我的理解:幂等就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的
三、技术方案
1. 查询操作
查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作
2. 删除操作
删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个)
3.唯一索引,防止新增脏数据
比如:支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建资金账户多个,那么给资金账户表中的用户ID加唯一索引,所以一个用户新增成功一个资金账户记录
要点:
唯一索引或唯一组合索引来防止新增数据存在脏数据
(当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可)
4. token机制,防止页面重复提交
业务要求:
页面的数据只能被点击提交一次
发生原因:
由于重复点击或者网络重发,或者nginx重发等情况会导致数据被重复提交
解决办法:
集群环境:采用token加redis(redis单线程的,处理需要排队)
单JVM环境:采用token加redis或token加jvm内存
处理流程:
1. 数据提交前要向服务的申请token,token放到redis或jvm内存,token有效时间
2. 提交后后台校验token,同时删除token,生成新的token返回
token特点:
要申请,一次有效性,可以限流
注意:redis要用删除操作来判断token,删除成功代表token校验通过,如果用select+delete来校验token,存在并发问题,不建议使用
5. 悲观锁
获取数据的时候加锁获取
select * from table_xxx where id='xxx' for update;
注意:id字段一定是主键或者唯一索引,不然是锁表,会死人的
悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用
6. 乐观锁
乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。
乐观锁的实现方式多种多样可以通过version或者其他状态条件:
1. 通过版本号实现
update table_xxx set name=#name#,version=version+1 where version=#version#
如下图(来自网上):
2. 通过条件限制
update table_xxx set avai_amount=avai_amount-#subAmount# where avai_amount-#subAmount# >= 0
要求:quality-#subQuality# >= ,这个情景适合不用版本号,只更新是做数据安全校验,适合库存模型,扣份额和回滚份额,性能更高
注意:乐观锁的更新操作,最好用主键或者唯一索引来更新,这样是行锁,否则更新时会锁表,上面两个sql改成下面的两个更好
update table_xxx set name=#name#,version=version+1 where id=#id# and version=#version#
update table_xxx set avai_amount=avai_amount-#subAmount# where id=#id# and avai_amount-#subAmount# >= 0
7. 分布式锁
还是拿插入数据的例子,如果是分布是系统,构建全局唯一索引比较困难,例如唯一性的字段没法确定,这时候可以引入分布式锁,通过第三方的系统(redis或zookeeper),在业务系统插入数据或者更新数据,获取分布式锁,然后做操作,之后释放锁,这样其实是把多线程并发的锁的思路,引入多多个系统,也就是分布式系统中得解决思路。
要点:某个长流程处理过程要求不能并发执行,可以在流程执行之前根据某个标志(用户ID+后缀等)获取分布式锁,其他流程执行时获取锁就会失败,也就是同一时间该流程只能有一个能执行成功,执行完成后,释放分布式锁(分布式锁要第三方系统提供)
8. select + insert
并发不高的后台系统,或者一些任务JOB,为了支持幂等,支持重复执行,简单的处理方法是,先查询下一些关键数据,判断是否已经执行过,在进行业务处理,就可以了
注意:核心高并发流程不要用这种方法
9. 状态机幂等
在设计单据相关的业务,或者是任务相关的业务,肯定会涉及到状态机(状态变更图),就是业务单据上面有个状态,状态在不同的情况下会发生变更,一般情况下存在有限状态机,这时候,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样的话,保证了有限状态机的幂等。
注意:订单等单据类业务,存在很长的状态流转,一定要深刻理解状态机,对业务系统设计能力提高有很大帮助
10. 对外提供接口的api如何保证幂等
如银联提供的付款接口:需要接入商户提交付款请求时附带:source来源,seq序列号
source+seq在数据库里面做唯一索引,防止多次付款,(并发时,只能处理一个请求)
重点:
对外提供接口为了支持幂等调用,接口有两个字段必须传,一个是来源source,一个是来源方序列号seq,这个两个字段在提供方系统里面做联合唯一索引,这样当第三方调用时,先在本方系统里面查询一下,是否已经处理过,返回相应处理结果;没有处理过,进行相应处理,返回结果。注意,为了幂等友好,一定要先查询一下,是否处理过该笔业务,不查询直接插入业务系统,会报错,但实际已经处理了。
总结:
幂等性应该是合格程序员的一个基因,在设计系统时,是首要考虑的问题,尤其是在像支付宝,银行,互联网金融公司等涉及的都是钱的系统,既要高效,数据也要准确,所以不能出现多扣款,多打款等问题,这样会很难处理,用户体验也不好
欢迎大家关注我的公众号:
评论
9 楼
ryuhi
2018-04-18
一个是来源source,一个是来源方序列号seq
这两个数据要怎么生成比较好?比如来源方序列号seq,要采用UUID吗?
这两个数据要怎么生成比较好?比如来源方序列号seq,要采用UUID吗?
8 楼
xuezhongyu01
2018-04-16
无量 写道
Master-Gao 写道
理论感觉还行,可以代码我还是不会写。。。
写的很清楚了,大致代码逻辑已经有了,自己还写不出来啊,我贴一堆代码在博客里也不太好哦,大家对读代码兴趣不大
对抄代码兴趣蛮大的,ctrl+c ctrl+v嘛,哈哈~
7 楼
无量
2018-04-09
Master-Gao 写道
理论感觉还行,可以代码我还是不会写。。。
写的很清楚了,大致代码逻辑已经有了,自己还写不出来啊,我贴一堆代码在博客里也不太好哦,大家对读代码兴趣不大
6 楼
Master-Gao
2017-11-25
理论感觉还行,可以代码我还是不会写。。。
5 楼
ls8023
2017-06-21
写的不错
4 楼
无量
2017-01-28
chengcheng222e 写道
写的比较详细,确实都是在对数据唯一性很强的系统中,这些都是铁律啊。
是的,程序设计的潜在法律
3 楼
chengcheng222e
2017-01-16
写的比较详细,确实都是在对数据唯一性很强的系统中,这些都是铁律啊。
2 楼
xinguangduan
2016-05-04
有实战意义
1 楼
liubey
2016-02-15
写得很棒!
发表评论
-
区块链!每个人都要了解下--十分钟洞见区块链的前世今生
2018-04-27 12:32 735区块链!每个人都要了解下--十分钟洞见区块链的前世今生 ... -
区块链!每个人都要了解下--十分钟洞见区块链的前世今生
2018-04-27 12:09 0为啥要讲区块链呢,因为它太火了,火到什么程度呢 ... -
项目打包,报软件包、类不存在问题排查过程
2017-05-16 17:13 5890项目打包报,软件包、类不存在问题排查过程 一、背景 ... -
海量数据存储--分库分表策略详解
2017-04-12 19:59 7268海量数据存储--分库分表策略详解 一、背景: 系统刚 ... -
jstack详解
2017-02-17 11:15 1823jstack http://www.open-open.co ... -
jdk-源码中的一些坑
2017-02-13 15:17 1165jdk-源码中的一些坑 1. Runnable接口的命名简直 ... -
一次mysql死锁的排查过程
2016-11-21 10:04 11194一次mysql死锁的排查过程 一、背景 17号晚上要吃饭 ... -
JVM调优:选择合适的GC collector (三)
2016-11-15 20:51 1207CMS Collector 在很多地方,CMS Collec ... -
JVM调优:选择合适的GC collector (二)
2016-11-15 20:47 944http://blog.csdn.net/historya ... -
JVM调优:选择合适的GC collector (一)
2016-11-15 20:45 1225http://blog.csdn.net/historyas ... -
jstat查看gc情况
2016-11-10 10:11 2441jps(Java Virtual Machine Proces ... -
tomcat源码学习(一) eclipse导入tomcat源码
2016-10-31 20:05 13071. 到官网下载Tomcat源代码,这里用到的是apache- ... -
深入分析ClassLoader
2016-10-27 23:27 710转(原文http://blog.csdn.net/xya ... -
业务架构模板
2016-10-20 19:56 1815业务架构模板 默认一个高大上的业务系统需要具备的技术点和对应 ... -
如何写一个强壮的JOB任务
2016-10-18 15:00 2968如何写一个强壮的JOB任务 1. JOB跑一半断电了,不能产 ... -
mybatis.xml中sql编写规范
2016-10-18 14:54 6192一、越少的代码,越强悍的功能,xml里面应该6个sql语句就够 ... -
数据库设计规范
2016-10-17 23:29 64971. 数据库设计基本规范 领域驱动表内容 ... -
全局主键生成器-支持单JVM1秒近1000万订单生成
2016-05-03 20:46 5602全局主键生成器 介绍: 相对于DB自增序列的全局主键生成器, ... -
解决并发下累计的问题
2016-04-25 11:58 2070package com.tongbanjie.trade.te ... -
系统开发中的坑
2016-03-15 15:39 2733系统开发中的坑 这个是在公司分享的一个ppt,整理下发到博客里 ...
相关推荐
1. 什么是幂等性 2. 常见的幂等性问题 1. public void like(Article article, User user) { 2. //检查是
互联网高并发解决方案-基于Hystrix实现服务隔离与降级互联网高并发解决方案-基于Hystrix实现服务隔离与降级互联网高并发解决方案-基于Hystrix实现服务隔离与降级互联网高并发解决方案-基于Hystrix实现服务隔离与降级...
Java并发编程与高并发解决方案-学习笔记
高并发下的接口幂等性解决方案.docx
1.不多发 2.不少发 1.下单 2.下单同时预占库存 3.付 4.付成功真正减扣库存 5.取消订单 6.回退预占库存 1. 户点击过快,重复提交两次 2. 络
高并发下接口幂等性解决方案.docx
亿级流量网站架构核心技术--跟开涛学搭建高可用高并发系统 ,有目录,无水印
Java并发编程与高并发解决方案-学习笔记-www.itmuch.com
高并发-高负载数据库解决方案(也有针对SQL).pdf高并发-高负载数据库解决方案(也有针对SQL).pdf高并发-高负载数据库解决方案(也有针对SQL).pdf高并发-高负载数据库解决方案(也有针对SQL).pdf高并发-高负载数据库解决...
1.3.2 幂等设计 13 1.3.3 流程可定义 13 1.3.4 状态与状态机 13 1.3.5 后台系统操作可反馈 14 1.3.6 后台系统审批化 14 1.3.7 文档和注释 14 1.3.8 备份 14 1.4 总结 14 第2部分高可用 17 2 负载均衡与反向代理 18 ...
高并发解决方案-提升高并发量服务器性能解决思路.docx
亿级流量网站架构核心技术-跟开涛学搭建高可用高并发系统
高并发解决方案-负载均衡.docx
Linux环境下高并发web服务器-的设计与实现.docx
亿级流量网站架构核心技术 跟开涛学搭建高可用高并发系统.pdf(带书签)
Java_并发核心编程-中文翻译_英文原版开源项目JNA-中文翻译版
JAVA并发编程与高并发解决方案-并发编程四之J.U.C之AQS.docx
高并发下接口幂等性解决方案_2.docx
浅谈高并发下接口幂等性解决方案.docx
实战Java高并发程序设计-随书代码 完整版