1. 程式人生 > >mysql悲觀鎖總結和實踐for update

mysql悲觀鎖總結和實踐for update

最近學習了一下資料庫的悲觀鎖和樂觀鎖,根據自己的理解和網上參考資料總結如下:

悲觀鎖介紹(百科):

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

使用場景舉例:以MySQL InnoDB為例

商品goods表中有一個欄位status,status為1代表商品未被下單,status為2代表商品已經被下單,那麼我們對某個商品下單時必須確保該商品status為1。假設商品的id為1。

1如果不採用鎖,那麼操作方法如下:

//1.查詢出商品資訊

select status from t_goods where id=1;

//2.根據商品資訊生成訂單

insert into t_orders (id,goods_id) values (null,1);

//3.修改商品status為2

update t_goods set status=2;

上面這種場景在高併發訪問的情況下很可能會出現問題。

前面已經提到,只有當goods status為1時才能對該商品下單,上面第一步操作中,查詢出來的商品status為1。但是當我們執行第三步Update操作的時候,有可能出現其他人先一步對商品下單把goods status修改為2了,但是我們並不知道資料已經被修改了,這樣就可能造成同一個商品被下單2次,使得資料不一致。所以說這種方式是不安全的。

2使用悲觀鎖來實現:

在上面的場景中,商品資訊從查詢出來到修改,中間有一個處理訂單的過程,使用悲觀鎖的原理就是,當我們在查詢出goods資訊後就把當前的資料鎖定,直到我們修改完畢後再解鎖。那麼在這個過程中,因為goods被鎖定了,就不會出現有第三者來對其進行修改了。

注:要使用悲觀鎖,我們必須關閉mysql資料庫的自動提交屬性,因為MySQL預設使用autocommit模式,也就是說,當你執行一個更新操作後,MySQL會立刻將結果進行提交。

我們可以使用命令設定MySQL為非autocommit模式:

set autocommit=0;

設定完autocommit後,我們就可以執行我們的正常業務了。具體如下:

//0.開始事務

begin;/begin work;/start transaction; (三者選一就可以)

//1.查詢出商品資訊

select status from t_goods where id=1 for update;

//2.根據商品資訊生成訂單

insert into t_orders (id,goods_id) values (null,1);

//3.修改商品status為2

update t_goods set status=2;

//4.提交事務

commit;/commit work;

注:上面的begin/commit為事務的開始和結束,因為在前一步我們關閉了mysql的autocommit,所以需要手動控制事務的提交,在這裡就不細表了。

上面的第一步我們執行了一次查詢操作:select status from t_goods where id=1 for update;

與普通查詢不一樣的是,我們使用了select…for update的方式,這樣就通過資料庫實現了悲觀鎖。此時在t_goods表中,id為1的 那條資料就被我們鎖定了,其它的事務必須等本次事務提交之後才能執行。這樣我們可以保證當前的資料不會被其它事務修改。

注:需要注意的是,在事務中,只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 同一筆資料時會等待其它事務結束後才執行,一般SELECT ... 則不受此影響。拿上面的例項來說,當我執行select status from t_goods where id=1 for update;後。我在另外的事務中如果再次執行select status from t_goods where id=1 for update;則第二個事務會一直等待第一個事務的提交,此時第二個查詢處於阻塞的狀態,但是如果我是在第二個事務中執行select status from t_goods where id=1;則能正常查詢出資料,不會受第一個事務的影響。

補充:MySQL select…for update的Row Lock與Table Lock

上面我們提到,使用select…for update會把資料給鎖住,不過我們需要注意一些鎖的級別,MySQL InnoDB預設Row-Level Lock,所以只有「明確」地指定主鍵,MySQL 才會執行Row lock (只鎖住被選取的資料) ,否則MySQL 將會執行Table Lock (將整個資料表單給鎖住)。

舉例說明:

資料庫表t_goods,包括id,status,name三個欄位,id為主鍵,資料庫中記錄如下;

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

注:為了測試資料庫鎖,我使用兩個console來模擬不同的事務操作,分別用console1、console2來表示。 

例1: (明確指定主鍵,並且有此資料,row lock)

console1:查詢出結果,但是把該條資料鎖定了

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where id=1 for update;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  1 |      1 | 道具 |  
  6. +----+--------+------+  
  7. 1 row in set  
  8. mysql>  

console2:查詢被阻塞

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where id=1 for update;  

console2:如果console1長時間未提交,則會報錯

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where id=1 for update;  
  2. ERROR 1205 : Lock wait timeout exceeded; try restarting transaction  

例2: (明確指定主鍵,若查無此資料,無lock)

console1:查詢結果為空

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where id=3 for update;  
  2. Empty set  

console2:查詢結果為空,查詢無阻塞,說明console1沒有對資料執行鎖定

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where id=3 for update;  
  2. Empty set  

例3: (無主鍵,table lock)

console1:查詢name=道具 的資料,查詢正常

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where name='道具' for update;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  1 |      1 | 道具 |  
  6. +----+--------+------+  
  7. 1 row in set  
  8. mysql>  

console2:查詢name=裝備 的資料,查詢阻塞,說明console1把表給鎖住了

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where name='裝備' for update;  

console2:若console1長時間未提交,則查詢返回為空

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where name='裝備' for update;  
  2. Query OK, -1 rows affected  

例4: (主鍵不明確,table lock)

console1:查詢正常

Sql程式碼  收藏程式碼
  1. mysql> begin;  
  2. Query OK, 0 rows affected  
  3. mysql> select * from t_goods where id>0 for update;  
  4. +----+--------+------+  
  5. | id | status | name |  
  6. +----+--------+------+  
  7. |  1 |      1 | 道具 |  
  8. |  2 |      1 | 裝備 |  
  9. +----+--------+------+  
  10. rows in set  
  11. mysql>  

console2:查詢被阻塞,說明console1把表給鎖住了

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where id>1 for update;  

例5: (主鍵不明確,table lock)

console1:

Sql程式碼  收藏程式碼
  1. mysql> begin;  
  2. Query OK, 0 rows affected  
  3. mysql> select * from t_goods where id<>1 for update;  
  4. +----+--------+------+  
  5. | id | status | name |  
  6. +----+--------+------+  
  7. |  2 |      1 | 裝備 |  
  8. +----+--------+------+  
  9. 1 row in set  
  10. mysql>  

console2:查詢被阻塞,說明console1把表給鎖住了

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where id<>2 for update;  

console1:提交事務

Sql程式碼  收藏程式碼
  1. mysql> commit;  
  2. Query OK, 0 rows affected  

console2:console1事務提交後,console2查詢結果正常

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where id<>2 for update;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  1 |      1 | 道具 |  
  6. +----+--------+------+  
  7. 1 row in set  
  8. mysql>  

以上就是關於資料庫主鍵對MySQL鎖級別的影響例項,需要注意的是,除了主鍵外,使用索引也會影響資料庫的鎖定級別

舉例:

我們修改t_goods表,給status欄位建立一個索引

修改id為2的資料的status為2,此時表中資料為:

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

例6: (明確指定索引,並且有此資料,row lock)

console1:

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where status=1 for update;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  1 |      1 | 道具 |  
  6. +----+--------+------+  
  7. 1 row in set  
  8. mysql>  

console2:查詢status=1的資料時阻塞,超時後返回為空,說明資料被console1鎖定了

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where status=1 for update;  
  2. Query OK, -1 rows affected  

console2:查詢status=2的資料,能正常查詢,說明console1只鎖住了行,未鎖表

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where status=2 for update;  
  2. +----+--------+------+  
  3. | id | status | name |  
  4. +----+--------+------+  
  5. |  2 |      2 | 裝備 |  
  6. +----+--------+------+  
  7. 1 row in set  
  8. mysql>  

例7: (明確指定索引,若查無此資料,無lock)

console1:查詢status=3的資料,返回空資料

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where status=3 for update;  
  2. Empty set  

console2:查詢status=3的資料,返回空資料

Sql程式碼  收藏程式碼
  1. mysql> select * from t_goods where status=3 for update;  
  2. Empty set  

以上就是關於我對資料庫悲觀鎖的理解和總結,有不對的地方歡迎拍磚,下一次會帶來資料庫樂觀鎖的總結和實踐

參考資料:

MySQL事務與鎖定命令:http://www.docin.com/p-16805970.html

悲觀鎖:http://www.cnblogs.com/chenwenbiao/archive/2012/06/06/2537508.html 

相關推薦

mysql悲觀總結實踐for update

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

mysql悲觀總結實踐

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

MySql悲觀總結實踐

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

mysql樂觀總結實踐

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

Mysqlmysql樂觀總結實踐

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

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

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

mysql(for update)悲觀總結實踐

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

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

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

mysql悲觀以及樂觀總結實踐

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

[MySQL] 行級SELECT ... LOCK IN SHARE MODE SELECT ... FOR UPDATE

一、譯文 If you query data and then insert or update related data within the same transaction, the regular SELECT statement does not

MySql悲觀樂觀總結

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

mysql lock in share mode select for update

原文連結:http://blog.csdn.net/d6619309/article/details/52688250 工作需要,接觸到以下兩個MySQL sql語法: select lock in share mode select for update 1

mysql悲觀樂觀

一、悲觀鎖 1、排它鎖,當事務在操作資料時把這部分資料進行鎖定,直到操作完畢後再解鎖,其他事務操作才可操作該部分資料。這將防止其他程序讀取或修改表中的資料。 2、實現:大多數情況下依靠資料庫的鎖機制實現 一般使用 select ...for update

mysql悲觀處理贈品庫存超賣的情況

count func this set private flex tail and 預測 處理庫存超賣的情況前,先了解下什麽是樂觀鎖和悲觀鎖,下面的幾篇博客已經介紹的比較詳細了,我就不在贅述其原理了 【MySQL】悲觀鎖&樂觀鎖 對mysql樂觀鎖、悲觀鎖、共享

深入理解SELECT ... LOCK IN SHARE MODESELECT ... FOR UPDATE

概念和區別 SELECT ... LOCK IN SHARE MODE走的是IS鎖(意向共享鎖),即在符合條件的rows上都加了共享鎖,這樣的話,其他session可以讀取這些記錄,也可以繼續新增IS鎖,但是無法修改這些記錄直到你這個加鎖的session執行完成(否則直接鎖等待超時)。 SELE

mysql 悲觀、共享、排它、行

悲觀鎖 與樂觀鎖相對應的就是悲觀鎖了。悲觀鎖就是在操作資料時,認為此操作會出現資料衝突,所以在進行每次操作時都要通過獲取鎖才能進行對相同資料的操作,這點跟java中的synchronized很相似,所以悲觀鎖需要耗費較多的時間。另外與樂觀鎖相對應的,悲觀鎖是由資料庫自己實現了的,要用的時候,我們

mysql分析與實踐

一序      本文分為兩個部分,第一部分主要基於何登成大神的文章。何博士作為阿里資料庫核心團隊大神。文章更是深入淺出。膜拜一下:原文地址如下  http://hedengcheng.com/?p=771    第二部分介紹常見的實踐注意事項。 二 背景   MVCC

Mysql-InnoDB的最佳實踐

Mysql自稱為世界上最收歡迎的開源(GPL協議)資料庫, 可以有效幫助企業構建高效能,高可用的應用程式; 其自身提高了兩種資料庫引擎MyISAM和InnoDB, 然後由於InnoDB支援事物及行級鎖, 因此被很多網際網路公司優先選用; 本篇博文主要介紹Mysq

MySQL資料庫優化(三)——MySQL悲觀&&樂觀(併發控制)

一、悲觀鎖     1、排它鎖,當事務在操作資料時把這部分資料進行鎖定,直到操作完畢後再解鎖,其他事務操作才可操作該部分資料。這將防止其他程序讀取或修改表中的資料。     2、實現:大多數情況下依靠資料庫的鎖機制實現      一般使用 select ...for upd

mysql悲觀,高併發

1.高併發的時候有2種處理 1)後端進行執行緒安全處理,synchrnoized,還有其他不同粒度的鎖 2)在資料庫設定鎖,當你讀的時候,不允許其他人修改。可以用mysql的悲觀鎖 2.悲觀鎖 select * from 表名 for update for update很重