mysql樂觀鎖是相對悲觀鎖而言,樂觀鎖假設認為資料一般情況下不會造成衝突,所以在資料進行提交更新的時候,才會正式對資料的衝突與否進行偵測,如果發現衝突了,則讓傳回使用者錯誤的訊息,讓使用者決定如何做。
mysql樂觀鎖是( Optimistic Locking )相對悲觀鎖定而言,樂觀鎖定假設認為資料一般情況下不會造成衝突,所以在資料進行提交更新的時候,才會正式對資料的衝突與否進行檢測,如果發現衝突了,則讓返回使用者錯誤的訊息,讓使用者決定如何做。
那麼我們要如何實現樂觀鎖呢,一般來說有以下2種方式:
#1、使用資料版本(Version)記錄機制實作,這是樂觀鎖最常用的一種實作方式。何謂數據版本?即為資料增加一個版本標識,一般是透過為資料庫表增加一個數字類型的 “version” 字段來實現。當讀取資料時,將version欄位的值一同讀出,資料每更新一次,對此version值加一。
當我們提交更新的時候,判斷資料庫表對應記錄的當前版本資訊與第一次取出的version值進行比對,如果資料庫表目前版本號與第一次取出的version值相等,則予以更新,否則認為是過期資料。用下面的一張圖來說明:
如如上圖所示,如果更新操作順序執行,則資料的版本(version)依序遞增,不會產生衝突。但如果發生有不同的業務操作對同一版本的資料進行修改,那麼,先提交的操作(圖中B)會把資料version更新為2,當A在B之後提交更新時發現資料的version已經被修改了,那麼A的更新操作會失敗。
2、樂觀鎖定的第二種實作方式和第一種差不多,同樣是在需要樂觀鎖定控制的table中增加一個字段,名稱無所謂,欄位類型使用時間戳記(timestamp), 和上面的version類似,也是在更新提交的時候檢查當前資料庫中資料的時間戳記和自己更新前取到的時間戳進行對比,如果一致則OK,否則就是版本衝突。
相關學習推薦:mysql影片教學
#使用範例:
以 MySQL InnoDB
為例
還是拿之前的實例來舉:商品goods表中有一個字段status,status為1代表商品未被下單,status為2代表商品已經下單,那麼我們對某個商品下單時必須確保該商品status為1。假設商品的id為1。
下單操作包含3步驟:
1、查詢出商品資訊
select (status,status,version) from t_goods where id=#{id}
2、根據商品資訊產生訂單
3、修改商品status為2
update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};
那麼為了使用樂觀鎖,我們先修改t_goods表,增加一個version字段,資料預設version值為1。
t_goods
表初始資料如下:
mysql> select * from t_goods; +----+--------+------+---------+ | id | status | name | version | +----+--------+------+---------+ | 1 | 1 | 道具 | 1 | | 2 | 2 | 装备 | 2 | +----+--------+------+---------+ 2 rows in set mysql>
對於樂觀鎖定的實現,我使用MyBatis來進行實踐,具體如下:
Goods實體類別:
/** * ClassName: Goods <br/> * Function: 商品实体. <br/> * date: 2013-5-8 上午09:16:19 <br/> * @author chenzhou1025@126.com */ public class Goods implements Serializable { /** * serialVersionUID:序列化ID. */ private static final long serialVersionUID = 6803791908148880587L; /** * id:主键id. */ private int id; /** * status:商品状态:1未下单、2已下单. */ private int status; /** * name:商品名称. */ private String name; /** * version:商品数据版本号. */ private int version; @Override public String toString(){ return "good id:"+id+",goods status:"+status+",goods name:"+name+",goods version:"+version; } //setter and getter }
GoodsDao
/** * updateGoodsUseCAS:使用CAS(Compare and set)更新商品信息. <br/> * * @author chenzhou1025@126.com * @param goods 商品对象 * @return 影响的行数 */ int updateGoodsUseCAS(Goods goods);
mapper.xml
<update id="updateGoodsUseCAS" parameterType="Goods"> <![CDATA[ update t_goods set status=#{status},name=#{name},version=version+1 where id=#{id} and version=#{version} ]]> </update>
#GoodsDaoTest測試類
@Test public void goodsDaoTest(){ int goodsId = 1; //根据相同的id查询出商品信息,赋给2个对象 Goods goods1 = this.goodsDao.getGoodsById(goodsId); Goods goods2 = this.goodsDao.getGoodsById(goodsId); //打印当前商品信息 System.out.println(goods1); System.out.println(goods2); //更新商品信息1 goods1.setStatus(2);//修改status为2 int updateResult1 = this.goodsDao.updateGoodsUseCAS(goods1); System.out.println("修改商品信息1"+(updateResult1==1?"成功":"失败")); //更新商品信息2 goods1.setStatus(2);//修改status为2 int updateResult2 = this.goodsDao.updateGoodsUseCAS(goods1); System.out.println("修改商品信息2"+(updateResult2==1?"成功":"失败")); }
輸出結果:
good id:1,goods status:1,goods name:道具,goods version:1 good id:1,goods status:1,goods name:道具,goods version:1 修改商品信息1成功 修改商品信息2失败
說明:
在GoodsDaoTest
測試方法中,我們同時查出同一個版本的數據,賦給不同的goods對象,然後先修改good1對象然後執行更新操作,執行成功。然後我們修改goods2,執行更新操作時提示操作失敗。此時t_goods
表中資料如下:
mysql> select * from t_goods; +----+--------+------+---------+ | id | status | name | version | +----+--------+------+---------+ | 1 | 2 | 道具 | 2 | | 2 | 2 | 装备 | 2 | +----+--------+------+---------+ 2 rows in set mysql>
我們可以看到 id為1的資料version已經在第一次更新時修改為2了。所以我們更新good2時update where條件已經不匹配了,所以更新不會成功,具體sql如下:
update t_goods set status=2,version=version+1 where id=#{id} and version=#{version};
這樣我們就實現了樂觀鎖。
以上是mysql樂觀鎖是什麼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!