溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

mysql中的樂觀鎖是怎么實(shí)現(xiàn)的

發(fā)布時(shí)間:2020-06-29 17:53:50 來源:億速云 閱讀:1144 作者:元一 欄目:MySQL數(shù)據(jù)庫

mysql中的樂觀鎖是怎么實(shí)現(xiàn)的?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖機(jī)制采取了更加寬松的加鎖機(jī)制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機(jī)制實(shí)現(xiàn),以保證操作最大程度的獨(dú)占性。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對長事務(wù)而言,這樣的開銷往往無法承受。而樂觀鎖機(jī)制在一定程度上解決了這個問題。樂觀鎖,大多是基于數(shù)據(jù)版本( Version )記錄機(jī)制實(shí)現(xiàn)。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個版本標(biāo)識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表增加一個 "version" 字段來實(shí)現(xiàn)。讀取出數(shù)據(jù)時(shí),將此版本號一同讀出,之后更新時(shí),對此版本號加一。此時(shí),將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當(dāng)前版本號,則予以更新,否則認(rèn)為是過期數(shù)據(jù)。

優(yōu)點(diǎn):

從上面的例子可以看出,樂觀鎖機(jī)制避免了長事務(wù)中的數(shù)據(jù)庫加鎖開銷(操作員 A和操作員 B 操作過程中,都沒有對數(shù)據(jù)庫數(shù)據(jù)加鎖),大大提升了大并發(fā)量下的系統(tǒng)整體性能表現(xiàn)。

缺點(diǎn):

需要注意的是,樂觀鎖機(jī)制往往基于系統(tǒng)中的數(shù)據(jù)存儲邏輯,因此也具備一定的局限性,如在上例中,由于樂觀鎖機(jī)制是在我們的系統(tǒng)中實(shí)現(xiàn),來自外部系統(tǒng)的用戶余額更新操作不受我們系統(tǒng)的控制,因此可能會造成臟數(shù)據(jù)被更新到數(shù)據(jù)庫中。在系統(tǒng)設(shè)計(jì)階段,我們應(yīng)該充分考慮到這些情況出現(xiàn)的可能性,并進(jìn)行相應(yīng)調(diào)整(如將樂觀鎖策略在數(shù)據(jù)庫存儲過程中實(shí)現(xiàn),對外只開放基于此存儲過程的數(shù)據(jù)更新途徑,而不是將數(shù)據(jù)庫表直接對外公開)。

如何實(shí)現(xiàn)樂觀鎖呢,一般來說有以下2種方式:

1、使用數(shù)據(jù)版本(Version)記錄機(jī)制實(shí)現(xiàn),這是樂觀鎖最常用的一種實(shí)現(xiàn) 方式。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個版本標(biāo)識,一般是通過為數(shù)據(jù)庫表增加一個數(shù)字類型的 “version” 字段來實(shí)現(xiàn)。當(dāng)讀取數(shù)據(jù)時(shí),將version字段的值一同讀出,數(shù)據(jù)每更新一次,對此version值加一。

當(dāng)我們提交更新的時(shí)候,判斷數(shù)據(jù)庫表對應(yīng)記錄 的當(dāng)前版本信息與第一次取出來的version值進(jìn)行比對,如果數(shù)據(jù)庫表當(dāng)前版本號與第一次取出來的version值相等,則予以更新,否則認(rèn)為是過期數(shù) 據(jù)。用下面的一張圖來說明:

mysql中的樂觀鎖是怎么實(shí)現(xiàn)的

如上圖所示,如果更新操作順序執(zhí)行,則數(shù)據(jù)的版本(version)依次遞增,不會產(chǎn)生沖突。但是如果發(fā)生有不同的業(yè)務(wù)操作對同一版本的數(shù)據(jù)進(jìn)行修 改,那么,先提交的操作(圖中B)會把數(shù)據(jù)version更新為2,當(dāng)A在B之后提交更新時(shí)發(fā)現(xiàn)數(shù)據(jù)的version已經(jīng)被修改了,那么A的更新操作會失敗。

2、樂觀鎖定的第二種實(shí)現(xiàn)方式和第一種差不多,同樣是在需要樂觀鎖控制的table中增加一個字段,名稱無所謂,字段類型使用時(shí)間戳 (timestamp), 和上面的version類似,也是在更新提交的時(shí)候檢查當(dāng)前數(shù)據(jù)庫中數(shù)據(jù)的時(shí)間戳和自己更新前取到的時(shí)間戳進(jìn)行對比,如果一致則OK,否則就是版本沖突。

使用舉例:

MySQL InnoDB為例

還是拿之前的實(shí)例來舉:商品goods表中有一個字段status,status為1代表商品未被下單,status為2代表商品已經(jīng)被下單,那么我們對某個商品下單時(shí)必須確保該商品status為1。假設(shè)商品的id為1。

下單操作包括3步驟:

1、查詢出商品信息

select (status,status,version) from t_goods where id=#{id}

2、根據(jù)商品信息生成訂單

3、修改商品status為2

update t_goods 
set status=2,version=version+1
where id=#{id} and version=#{version};

那么為了使用樂觀鎖,我們首先修改t_goods表,增加一個version字段,數(shù)據(jù)默認(rèn)version值為1。

t_goods表初始數(shù)據(jù)如下:

mysql> select * from t_goods;  
+----+--------+------+---------+  
| id | status | name | version |  
+----+--------+------+---------+  
|  1 |      1 | 道具 |       1 |  
|  2 |      2 | 裝備 |       2 |  
+----+--------+------+---------+  
2 rows in set  
  
mysql>

對于樂觀鎖的實(shí)現(xiàn),我使用MyBatis來進(jìn)行實(shí)踐,具體如下:

Goods實(shí)體類:

/** 
 * ClassName: Goods <br/> 
 * Function: 商品實(shí)體. <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:商品狀態(tài):1未下單、2已下單. 
     */  
    private int status;  
      
    /** 
     * name:商品名稱. 
     */  
    private String name;  
      
    /** 
     * version:商品數(shù)據(jù)版本號. 
     */  
    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 影響的行數(shù) 
 */  
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;  
    //根據(jù)相同的id查詢出商品信息,賦給2個對象  
    Goods goods1 = this.goodsDao.getGoodsById(goodsId);  
    Goods goods2 = this.goodsDao.getGoodsById(goodsId);  
      
    //打印當(dāng)前商品信息  
    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?"成功":"失敗"));  
}

輸出結(jié)果:

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測試方法中,我們同時(shí)查出同一個版本的數(shù)據(jù),賦給不同的goods對象,然后先修改good1對象然后執(zhí)行更新操作,執(zhí)行成功。然后我們修改goods2,執(zhí)行更新操作時(shí)提示操作失敗。此時(shí)t_goods表中數(shù)據(jù)如下:

mysql> select * from t_goods;  
+----+--------+------+---------+  
| id | status | name | version |  
+----+--------+------+---------+  
|  1 |      2 | 道具 |       2 |  
|  2 |      2 | 裝備 |       2 |  
+----+--------+------+---------+  
2 rows in set  
  
mysql>

我們可以看到 id為1的數(shù)據(jù)version已經(jīng)在第一次更新時(shí)修改為2了。所以我們更新good2時(shí)update where條件已經(jīng)不匹配了,所以更新不會成功,具體sql如下:

update t_goods   
set status=2,version=version+1  
where id=#{id} and version=#{version};

這樣我們就實(shí)現(xiàn)了樂觀鎖。

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI