失效链接处理 |
“乐观?rdquo;L搞定高ƈ发下的幂{性问题实战视频教E?/strong> 专题帖子 公众号帖?/span> 视频教程 “乐观?rdquo;L搞定高ƈ发下的幂{性问题实战视频教E?/strong> 相关截图Q?br /> 主要内容Q?/strong>
什么是q等性?
q等Qidempotent、idempotenceQ是一个数学与计算机学概念Q常见于抽象代数中。在~程?一个幂{操作的特点是其L多次执行所产生的媄(jing)响均与一ơ执行的影响相同。幂{函敎ͼ或幂{方法,是指可以使用相同参数重复执行Qƈ能获得相同结果的函数。这些函C?x)?jing)响系l状态,也不用担?j)重复执行?x)对系l造成改变。例如,“getUsername()和setTrue()”函数是一个幂{函? 更复杂的操作q等保证是利用唯一交易?水?实现. 我的理解Q幂{就是一个操作,不论执行多少ơ,产生的效果和q回的结果都是一L(fng) ?/div>
高ƈ发下的幂{性问?/div>
q里以两个实例来看下高ƈ发下的幂{性问题;
一Q购实?/div>
购票实现程如下Q?/div>
step1Q查询是否有,有票的话Ql下一步,否则提示无票Q结束;
step2Q从用户账户扣除款Q?/div>
step3Q余减一操作Q?/div>
q里的话Q正常情冉|问题Q但是比如用戯l多点了(jin)几次Q或者网l问题导致的再或者多人同时购买的时候的q发情况下,step1步骤?x)有两个或者多个线E同时进入,q时候判断都是有的Q然后l进入step2Qstep3Q这时候,可能会(x)出现余票负数Q多卖的情况Q?/div>
二,充值实?/div>
充值实现流E如下:(x)
step1Q用戯入充值金额,h后端业务pȝQ?/div>
step2Q后端生成订单,订单状态是未支付,然后再请求第三方支付接口Q?/div>
step3Q用L(fng)认支付Q?/div>
step4Q第三方支付通过我方提供的回调接口异步通知支付l果Q?/div>
具体step4 demo代码如下Q?/div>
System.out.println("查询订单");
Order order = orderMapper.getByOrderId(orderId); // Ҏ(gu)订单id获取订单
if(order.getStatus()==0){ // 假如是未支付状?/div>
System.out.println("未支付状?quot;);
order.setStatus(1); // 讄支付成功状?/div>
System.out.println("更新支付状?..");
orderMapper.update(order); // 更新支付状?/div>
System.out.println("账户充?..");
userAccountMapper.addAmount(order.getAmount(),userAccount.getUserId()); // 账户充?/div>
System.out.println("充值完?..");
return true;
}else{ // 已经支付成功Q订单已处理
System.out.println("发现订单已处?quot;);
return true;
}
q个W四步是有缺L(fng)Q假如第三方支付pȝ问题或者网l问题,有多个线E同时执行进?nbsp;
Order order = orderMapper.getByOrderId(orderId);
Ҏ(gu)订单id查询订单信息Q发现status状态都是未支付Q所以都q入if里面Q这时候就出现?jin)̎户重复充值的情况Q?/div>
q等性问题ȝ
只要更新数据是依赖读取的数据作ؓ(f)基础条g的,当遇到高q发的时候,可能会(x)出现q等性问题;
又比如在更新数据不依赖查询的数据的就不会(x)有问题,例如修改用户的名Uͼ多h同时修改Q结果ƈ不依赖于之前的用户名字,q就不会(x)有ƈ发更新问题?/div>
q等性问题解x(chng)?/div>
关于q等性问题的解决Ҏ(gu)Q业界提供了(jin)很多解决Ҏ(gu)Q如单机pȝ的Java 同步锁,乐观锁,(zhn)观锁,分布式锁Q唯一性烦(ch)引,token机制防止面重复提交{,每种Ҏ(gu)各有利弊Q不q主的话,q是乐观锁和分布式锁q两个方案;
Java同步锁方?/div>
我们可以使用synchronized同步锁,把查询状态的代码和更新的代码放一个同步锁内,q样同一时刻只能有一个线E进入执行,{执行完其他U程才能q入Q这栯解决q等性问题,但是假如同步块里面的业务代码执行旉比较长,q样?x)严重?jing)响用户体验,和系l的吞吐量。所以不是最x(chng)案;
(zhn)观锁方?/div>
(zhn)观锁(Pessimistic LockQ,思义Q就是很(zhn)观Q每ơ去拿数据的时候都认ؓ(f)别h?x)修改,所以每ơ在拿数据的时候都?x)上锁,q样别hx(chng)q个数据׃(x)block直到它拿到锁?/div>
(zhn)观锁:(x)假定?x)发生ƈ发冲H,屏蔽一切可能违反数据完整性的操作?/div>
Java synchronized 属于?zhn)观锁的一U实玎ͼ每次U程要修Ҏ(gu)据时都先获得锁,保证同一时刻只有一个线E能操作数据Q其他线E则?x)被block?/div>
数据库的(zhn)观锁通过 for update 实现的;
select * from t_order where orderId=#{orderId} for update
(zhn)观锁用时一般伴随事务一起用,数据锁定旉可能?x)很长,影响用户体验和系l吞吐量Q所以一般也不采用?/div>
乐观锁方?/div>
乐观锁(Optimistic LockQ,思义Q就是很乐观Q每ơ去拿数据的时候都认ؓ(f)别h不会(x)修改Q所以不?x)上锁,但是在提交更新的时候会(x)判断一下在此期间别人有没有L新这个数据。乐观锁适用于读多写的应用场景Q这样可以提高吞吐量?/div>
乐观锁:(x)假设不会(x)发生q发冲突Q只在提交操作时(g)查是否违反数据完整性?/div>
乐观锁一般来说有以下2U方式:(x)
1. 使用数据版本QVersionQ记录机制实玎ͼq是乐观锁最常用的一U实现方式。何谓数据版本?即ؓ(f)数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当d数据Ӟversion字段的g同读出,数据每更Cơ,Ҏ(gu)version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与W一ơ取出来的versionD行比对,如果数据库表当前版本号与W一ơ取出来的version值相{,则予以更斎ͼ否则认ؓ(f)是过期数据?nbsp;
2. 使用旉戻ItimestampQ。乐观锁定的W二U实现方式和W一U差不多Q同h在需要乐观锁控制的table中增加一个字D,名称无所谓,字段cd使用旉戻ItimestampQ? 和上面的versioncMQ也是在更新提交的时候检查当前数据库中数据的旉戛_自己更新前取到的旉戌行对比,如果一致则OKQ否则就是版本冲H?/div>
乐观锁方案在不媄(jing)响系l性能的情况下Q解决了(jin)高ƈ发幂{性问题,所以被得到q泛使用。唯一的缺点就是对代码h入R性?/div>
分布式锁
对于分布式系l,多个pȝ独立q行Q所以同步锁肯定是不行的Q对于分布式pȝQ可以用乐观锁或者分布式锁来解决q等性问题;
具体Ҏ(gu)有:(x)
1. Z~存QRedis{)(j)实现分布式锁Q?/div>
2. ZZookeeper实现分布式锁Q?/div>
Q备注:(x)下期我们?x)提供具体实现方案的视频教程Q感谢关注)(j)
|