记一次CAS思想在实际项目中的应用
Posted kuangdw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一次CAS思想在实际项目中的应用相关的知识,希望对你有一定的参考价值。
记CAS思想在实际开发中的一次应用
可能我们大多数人都懂CAS的原理,但是在实际开发中却是比较少真正用到它。本人在一次实际开发中还就真用到了,但是最后采用的解决方案感觉还不是最好的。下面就分享一下我遇到的问题和采用的解决方案吧,希望对遇到同样问题的朋友起到一点点灵感启发。
一、CAS原理简介
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){
1、if select from order where orderId = order.getOrderId() == null? 成立则return 订单已存在,否则往下走
2、if select from produce_orderid == null ?
成立则:insert into produce_orderid values(2020-04-30,2020043000000001);
insert into order values(2020043000000001,订单的其他信息...);
没有发生异常则返回成功,否则事务回滚返回失败
否则往下走
3、重点步骤:
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要保证是相等的。再来谈谈这种方案的缺点吧,其实是挺明显:进行的数据库操作次数较多,不适合并发量大的情况。但是话又说回来了,一般订单的订单号(流水号)的生成方式不会按这样的规则来,流水号的生成往往都是使用时间戳+随机数的方式来生成,如果并发量很大,可以考虑将随机数的位数设置得大一些,这样就几乎不会产生冲突了。
以上是关于记一次CAS思想在实际项目中的应用的主要内容,如果未能解决你的问题,请参考以下文章