1. 程式人生 > >mysql樂觀鎖總結和實踐(含demo例子)

mysql樂觀鎖總結和實踐(含demo例子)

上一篇文章《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 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表初始資料如下:

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods;  
  2. +----+--------+------+---------+  
  3. | id | status | name | version |  
  4. +----+--------+------+---------+  
  5. |  1 |      1 | 道具 |       1 |  
  6. |  2 |      2 | 裝備 |       2 |  
  7. +----+--------+------+---------+  
  8. rows in set  
  9. mysql>  

對於樂觀鎖的實現,我使用MyBatis來進行實踐,具體如下:

Goods實體類:

Java程式碼  收藏程式碼
  1. /** 
  2.  * ClassName: Goods <br/> 
  3.  * Function: 商品實體. <br/> 
  4.  * date: 2013-5-8 上午09:16:19 <br/> 
  5.  * @author [email protected] 
  6.  */  
  7. public class Goods implements Serializable {  
  8.     /** 
  9.      * serialVersionUID:序列化ID. 
  10.      */  
  11.     private static final long serialVersionUID = 6803791908148880587L;  
  12.     /** 
  13.      * id:主鍵id. 
  14.      */  
  15.     private int id;  
  16.     /** 
  17.      * status:商品狀態:1未下單、2已下單. 
  18.      */  
  19.     private int status;  
  20.     /** 
  21.      * name:商品名稱. 
  22.      */  
  23.     private String name;  
  24.     /** 
  25.      * version:商品資料版本號. 
  26.      */  
  27.     private int version;  
  28.     @Override  
  29.     public String toString(){  
  30.         return "good id:"+id+",goods status:"+status+",goods name:"+name+",goods version:"+version;  
  31.     }  
  32.     //setter and getter  
  33. }  

GoodsDao

Java程式碼  收藏程式碼
  1. /** 
  2.  * updateGoodsUseCAS:使用CAS(Compare and set)更新商品資訊. <br/> 
  3.  * 
  4.  * @author [email protected] 
  5.  * @param goods 商品物件 
  6.  * @return 影響的行數 
  7.  */  
  8. int updateGoodsUseCAS(Goods goods);  

mapper.xml

Xml程式碼  收藏程式碼
  1. <update id="updateGoodsUseCAS" parameterType="Goods">  
  2.     <![CDATA[ 
  3.         update t_goods 
  4.         set status=#{status},name=#{name},version=version+1 
  5.         where id=#{id} and version=#{version} 
  6.     ]]>  
  7. </update>  

GoodsDaoTest測試類

Java程式碼  收藏程式碼
  1. @Test  
  2. public void goodsDaoTest(){  
  3.     int goodsId = 1;  
  4.     //根據相同的id查詢出商品資訊,賦給2個物件  
  5.     Goods goods1 = this.goodsDao.getGoodsById(goodsId);  
  6.     Goods goods2 = this.goodsDao.getGoodsById(goodsId);  
  7.     //列印當前商品資訊  
  8.     System.out.println(goods1);  
  9.     System.out.println(goods2);  
  10.     //更新商品資訊1  
  11.     goods1.setStatus(2);//修改status為2  
  12.     int updateResult1 = this.goodsDao.updateGoodsUseCAS(goods1);  
  13.     System.out.println("修改商品資訊1"+(updateResult1==1?"成功":"失敗"));  
  14.     //更新商品資訊2  
  15.     goods1.setStatus(2);//修改status為2  
  16.     int updateResult2 = this.goodsDao.updateGoodsUseCAS(goods1);  
  17.     System.out.println("修改商品資訊2"+(updateResult2==1?"成功":"失敗"));  
  18. }  

輸出結果:

Shell程式碼  收藏程式碼
  1. good id:1,goods status:1,goods name:道具,goods version:1  
  2. good id:1,goods status:1,goods name:道具,goods version:1  
  3. 修改商品資訊1成功  
  4. 修改商品資訊2失敗  

(因為兩個物件goods1 goods2都是獲取到的同一個id的物件,物件中的version一樣,所以更新了一個後,sersion值不同了,goods2更新失敗了

說明:

在GoodsDaoTest測試方法中,我們同時查出同一個版本的資料,賦給不同的goods物件,然後先修改good1物件然後執行更新操作,執行成功。然後我們修改goods2,執行更新操作時提示操作失敗。此時t_goods表中資料如下:

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods;  
  2. +----+--------+------+---------+  
  3. | id | status | name | version |  
  4. +----+--------+------+---------+  
  5. |  1 |      2 | 道具 |       2 |  
  6. |  2 |      2 | 裝備 |       2 |  
  7. +----+--------+------+---------+  
  8. rows in set  
  9. mysql>   

我們可以看到 id為1的資料version已經在第一次更新時修改為2了。所以我們更新good2時update where條件已經不匹配了,所以更新不會成功,具體sql如下:

Sql程式碼  收藏程式碼
  1. update t_goods   
  2. set status=2,version=version+1  
  3. where id=#{id} and version=#{version};  

這樣我們就實現了樂觀鎖

轉載地址:http://chenzhou123520.iteye.com/blog/1863407

相關推薦

mysql樂觀總結實踐demo例子

上一篇文章《MySQL悲觀鎖總結和實踐》談到了MySQL悲觀鎖,但是悲觀鎖並不是適用於任何場景,它也有它存在的一些不足,因為悲觀鎖大多數情況下依靠資料庫的鎖機制實現,以保證操作最大程度的獨佔性。如果加鎖的時間過長,其他使用者長時間無法訪問,影響了程式的併發訪問性,同時這樣

mysql樂觀總結實踐

初始 serializa time suse auth 並發訪問 bubuko 主鍵 圖片 上一篇文章《MySQL悲觀鎖總結和實踐》談到了MySQL悲觀鎖,但是悲觀鎖並不是適用於任何場景,它也有它存在的一些不足, 因為悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大

Mysqlmysql樂觀總結實踐

per div batis 同時 tst value 新的 his 行修改 樂觀鎖介紹: 樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認為數據一般情況下不會造成沖突,所以在數據進行提交更新的時候,才會正式對數據的沖突與否進行檢測,如果發現沖

mysql悲觀以及樂觀總結實踐

注:本文乃轉載,原文作者@青蔥歲月 悲觀鎖介紹(百科): 悲觀鎖,正如其名,它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制(也只有資料庫層

mysql悲觀總結實踐

date mit begin 其它 需要 處理 mysql start 實例 悲觀鎖介紹(百科): 悲觀鎖,正如其名,它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個數據處理過程中,將數據處於鎖定狀態。悲觀鎖的實現,往

mysql悲觀總結實踐for update

最近學習了一下資料庫的悲觀鎖和樂觀鎖,根據自己的理解和網上參考資料總結如下: 悲觀鎖介紹(百科): 悲觀鎖,正如其名,它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,

mysql(for update)悲觀總結實踐分散式

  悲觀鎖,正如其名,它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制(也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否

MySql悲觀總結實踐

數據表 date操作 設置 下單 開始 說明 AR 根據 業務 mysql(for update)悲觀鎖總結與實踐 https://blog.csdn.net/zmx729618/article/details/52701972 悲觀鎖,正如其名,它指的是對數據被外界(包括

基於 MySQL 的數據庫實踐擴展運算

之間 art history 上進 簡化 source 希望 AC 顯示 select 中的通配符 星號 * 可以用在 select 子句中表示所有的屬性。 mysql> select instructor.* -> from instructor, t

Golang在視訊直播平臺的高效能實踐PPT下載

熊貓 TV 是一家視訊直播平臺,先介紹下我們系統執行的環境,下面這 6 大服務只是我們幾十個服務中的一部分,由於併發量與重要性比較高,所以成為 golang 小試牛刀的首批高效能高併發服務。 把大服務拆細, 然後服務化獨立部署,更容易簡化部署,也容易單點細節優化

路徑最短問題演算法總結實現Floyd,Dijkstra,SPFA

題目描述:求兩個點之間的最短路徑輸入:兩個整數n,m(1<=n,m<=100)n的含義是節點的個數,m的含義是邊的個數,接下來的m行輸入三個整數i j c,分別表示開始結束節點和之間的費用輸出:從1到n節點之間的路徑長度Floyd演算法:使用二維陣列ans[i][

Android 二維碼 生成識別Demo原始碼

  今天講一下目前移動領域很常用的技術——二維碼。現在大街小巷、各大網站都有二維碼的蹤跡,不管是IOS、Android、WP都有相關支援的軟體。之前我就想了解二維碼是如何工作,最近因為工作需要使用相關技術,所以做了初步瞭解。今天主要是講解如何使用ZXing庫,生成和識別二維碼。這篇文章實用性為主,理

Three.JS 新增燈光、材質陰影第一個例子

three.js中都多種燈光和材質,這裡只是添加了一種燈光,聚光燈spotLight。 首先構建一個spotLight物件: var spotLight = new THREE.SpotLight(0xFFFFFF); 指定光源的位置,從何處開始照

MySql悲觀樂觀總結

現在我有一個購買商品的需求,我們知道當我們購買商品時,後臺會進行減庫存和增加購買記錄的操作。我們分別在無鎖和樂觀鎖和悲觀鎖進行相應的程式碼演示來說明問題。     建表語句如下: CREATE TABLE `stock` ( `id` int(11) unsigned

轉: 【Java並發編程】之二十:並發新特性—Lock條件變量代碼

ets exc n) 否則 max 長時間 info trace space 簡單使用Lock鎖 Java 5中引入了新的鎖機制——Java.util.concurrent.locks中的顯式的互斥鎖:Lock接口,它提供了比synchronized更加廣泛的鎖

mysql(for update)悲觀總結實踐

target efi 過程 set ews 註意 IE ans 生成 悲觀鎖,正如其名,它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個數據處理過程中,將數據處於鎖定狀態。

ElasticSearch最佳入門實踐二十四partial update樂觀併發控制原理以及相關操作

(1)partial update內建樂觀鎖併發控制 partial update內部是自動執行之前所說的樂觀鎖的併發控制方案 兩個執行緒 都拿到了document資料和_version 使用傳過來的field更新document 執行緒B也在做partial update

mysql 樂觀悲觀

悲觀鎖與樂觀鎖是兩種常見的資源併發鎖設計思路,也是併發程式設計中一個非常基礎的概念。本文將對這兩種常見的鎖機制在資料庫資料上的實現進行比較系統的介紹。 悲觀鎖(Pessimistic Lock) 悲觀鎖的特點是先獲取鎖,再進行業務操作,即“悲觀”的認為獲取鎖是非常有可能失

CASMySql樂觀實現下單

CAS和MySql樂觀鎖實現下單 準備 建表t_order: CREATE TABLE `t_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `version` int(255) DEFAULT NULL, `stock`

mysql樂觀悲觀詳解

相信很多朋友在面試的時候,都會被問到樂觀鎖和悲觀鎖的問題,如果不清楚其概念和用法的情況下,相信很多朋友都會感覺很懵逼,那麼面試的結果也就不言而喻了。 那麼樂觀鎖和悲觀鎖到底是個什麼東西,用它能來做什麼呢? 相信大家都遇到這種場景,當很多人(一兩個人估計不行)同時對同一條資料做修改的時候,那麼資料