可能咱们大多数人都懂CAS的原理,可是在实际开发中倒是比较少真正用到它。本人在一次实际开发中还就真用到了,可是最后采用的解决方案感受还不是最好的。下面就分享一下我遇到的问题和采用的解决方案吧,但愿对遇到一样问题的朋友起到一点点灵感启发。redis
CAS(compare and set)实际上是一种乐观锁的思想,我的理解来看,感受能够将其大致上能够分为三步:
(1)从数据库中拿到要更改的数据,这里就记为oldValue吧,而后令指望值expectedValue=oldValue
(2)修改值:newVlue=oldValue+100
(3)更新值:再从数据库中拿到要更新的那个数据,这里就叫作当前oldValue,若是当前oldvalue等于expectedValue,就set oldValue=newValue并返回
不然循环(1)(2)直到当前oldValue等于expectedValue,而后再set oldValue=newValue并返回。数据库
在一次实际项目中碰到这么个问题:
首先,有一个订单表order,而后订单号的生成规则为:订单号 = 当前日期 + 0000 + 0001,现在天的第一笔订单的订单号为2020043000000001。而后后面的订单号单调递增,注意,order表中的订单号是惟一的。对于这个问题,咱们可能首先想到的解决方法是使用redis+设置过时时间来解决,即天天凌晨产生当天的第一个订单号如2020043000000000,而后根据新产生的订单依次递增订单号。这种方法很容易想到,可是我的感受redis中的订单号与数据库中order表中的订单号的一致性不是很好控制,因此没有采用这种方案。多线程
(1)建立两张表,订单表order,还有生成订单号的表produce_orderid;
(2)order表用来存储订单信息,produce_orderid表只有有两个字段:orderdate,maxorderid。orderdate表明当期日期,maxorderid为当天最大订单号;
(3)因此根据我的在(一)中简述的CAS原理,就知道如何用代码解决了这个问题了;并发
这里附上新增订单方法的伪代码吧:线程
@Transactional //两个表中的操做是一个事务 public Result addOrder(Order order){ 一、if select from order where orderId = order.getOrderId() == null? 成立则return 订单已存在,不然往下走 二、if select from produce_orderid == null ? 成立则:insert into produce_orderid values(2020-04-30,2020043000000001); insert into order values(2020043000000001,订单的其余信息...); 没有发生异常则返回成功,不然事务回滚返回失败 不然往下走 三、重点步骤: long oldvalue = select maxorderid from produce_orderid where orderdate = Date; long newvalue = oldvalue + 1; flag = update produce_orderid set maxorderid = newvalue where orderdate = Date and maxorderid=oldvalue; if(flag) { //若是更新成功说明没被他人改动,那么就在order表中新增订单并返回 insert into order values(newvalue,其余信息); 没有发生异常则返回成功,不然事务回滚返回失败; } //不然就说明被改动了,那么就进入循环 while(flag != true){ //循环直到更新成功退出 //将旧值+1,而后新值总比旧值大1 oldvalue ++; newvalue = oldvalue + 1; flag = (update produce_orderid set maxorderid=newvalue where orderdate=Date and maxorderid=oldvalue; } //结束循环说明更新成功,就能够在order表中新增订单了 insert into order values(newvalue,其余信息); 没有发生异常则返回成功,不然事务回滚返回失败; }
解决方案的大体思想都在上面描述出来了,实际代码只须要将SQL语句转化成响应的业务逻辑就好了。总之,就是使用CAS的思想和事务控制来保证在多线程新增订单的状况下:1)不会有重复的订单号,2)order表中一天当中的最新的订单号与produce_orderid表中的当期日期的maxorderid要保证是相等的。再来谈谈这种方案的缺点吧,实际上是挺明显:进行的数据库操做次数较多,不适合并发量大的状况。可是话又说回来了,通常订单的订单号(流水号)的生成方式不会按这样的规则来,流水号的生成每每都是使用时间戳+随机数的方式来生成,若是并发量很大,能够考虑将随机数的位数设置得大一些,这样就几乎不会产生冲突了。code