可能咱們大多數人都懂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