1. 程式人生 > 其它 >16、資料庫鎖機制

16、資料庫鎖機制

一、資料庫的鎖機制

什麼是鎖?為何要加入鎖機制?

鎖是計算機協調多個程序或執行緒併發訪問某一資源的機制,那為何要加入鎖機制呢?

因為在資料庫中,除了傳統的計算資源(如CPU、RAM、I/O等)的爭用以外,資料也是一種供需要使用者共享的資源。

當併發事務同時訪問一個共享的資源時,有可能導致資料不一致、資料無效等問題。

例如我們在資料庫的讀現象中介紹過,在併發訪問情況下,可能會出現髒讀、不可重複讀和幻讀等讀現象

為了應對這些問題,主流資料庫都提供了鎖機制,以及事務隔離級別的概念,而鎖機制可以將併發的資料訪問順序化,以保證資料庫中資料的一致性與有效性此外,鎖衝突也是影響資料庫併發訪問效能的一個重要因素,鎖對資料庫而言顯得尤其重要,也更加複雜。

併發控制

在電腦科學,特別是程式設計、作業系統、多處理機和資料庫等領域,併發控制(Concurrency control)是確保及時糾正由併發操作導致的錯誤的一種機制。

資料庫管理系統(DBMS)中的併發控制的任務是確保在多個事務同時存取資料庫中同一資料時不破壞事務的隔離性和統一性以及資料庫的統一性。下面舉例說明併發操作帶來的資料不一致性問題:

現有兩處火車票售票點,同時讀取某一趟列車車票資料庫中車票餘額為 X。兩處售票點同時賣出一張車票,同時修改餘額為 X -1寫回資料庫,這樣就造成了實際賣出兩張火車票而資料庫中的記錄卻只少了一張。 產生這種情況的原因是因為兩個事務讀入同一資料並同時修改,其中一個事務提交的結果破壞了另一個事務提交的結果,導致其資料的修改被丟失,破壞了事務的隔離性。併發控制要解決的就是這類問題。

封鎖、時間戳、樂觀併發控制(樂觀鎖)和悲觀併發控制(悲觀鎖)是併發控制主要採用的技術手段。

二、鎖的分類

一、按鎖的粒度劃分,可分為行級鎖、表級鎖、頁級鎖。(mysql支援)

二、按鎖級別劃分,可分為共享鎖、排他鎖

三、按使用方式劃分,可分為樂觀鎖、悲觀鎖

四、按加鎖方式劃分,可分為自動鎖、顯式鎖

五、按操作劃分,可分為DML鎖、DDL鎖

三、MySQL中的行級鎖,表級鎖,頁級鎖(粒度)

在DBMS中,可以按照鎖的粒度把資料庫鎖分為行級鎖(INNODB引擎)、表級鎖(MYISAM引擎)和頁級鎖(BDB引擎 )。

1、行級鎖

行級鎖是Mysql中鎖定粒度最細的一種鎖,表示只針對當前操作的行進行加鎖。行級鎖能大大減少資料庫操作的衝突。其加鎖粒度最小,但加鎖的開銷也最大。行級鎖分為共享鎖和排他鎖。

  • 特點:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。
  • 支援引擎:InnoDB
  • 行級鎖定分為行共享讀鎖(共享鎖)與行獨佔寫鎖(排他鎖)
共享鎖(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
排他鎖(X): SELECT * FROM table_name WHERE ... FOR UPDATE

1、對於insert、update、delete語句,InnoDB會自動給涉及的資料加鎖,而且是排他鎖(X)。

2、對於普通的select語句,InnoDB不會加任何鎖,需要我們手動自己加,可以加兩種型別的鎖。

2、表級鎖

表級鎖是MySQL中鎖定粒度最大的一種鎖,表示對當前操作的整張表加鎖,它實現簡單,資源消耗較少,被大部分MySQL引擎支援。最常使用的MYISAM與INNODB都支援表級鎖定。表級鎖定分為表共享讀鎖(共享鎖)與表獨佔寫鎖(排他鎖)。

  • 特點:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發出鎖衝突的概率最高,併發度最低。
  • 支援引擎:MyISAM、MEMORY、InNoDB
  • 分類:表級鎖定分為表共享讀鎖(共享鎖)與表獨佔寫鎖(排他鎖),如下所示
lock table 表名 read(write),表名 read(write),.....;
//給表加讀鎖或者寫鎖,例如
mysql> lock table employee write;
Query OK, 0 rows affected (0.00 sec)
mysql> show open tables where in_use>= 1;
+----------+----------+--------+-------------+
| Database | Table    | In_use | Name_locked |
+----------+----------+--------+-------------+
| ttt      | employee |      1 |           0 |
+----------+----------+--------+-------------+
1 row in set (0.00 sec)
mysql> unlock tables; -- UNLOCK TABLES釋放被當前會話持有的任何鎖
Query OK, 0 rows affected (0.00 sec)
 
mysql> show open tables where in_use>= 1;
Empty set (0.00 sec)
 
mysql>

3、頁級鎖

頁級鎖是MySQL中鎖定粒度介於行級鎖和表級鎖中間的一種鎖。表級鎖速度快,但衝突多,行級衝突少,但速度慢。所以取了折衷的頁級,一次鎖定相鄰的一組記錄。

特點:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。

四、行級鎖之共享鎖與排他鎖以及死鎖

行級鎖分為共享鎖和排他鎖兩種。

1、共享鎖

共享鎖又稱為讀鎖,簡稱S鎖,顧名思義,共享鎖就是多個事務對於同一資料可以共享一把鎖,都能訪問到資料,但是隻能讀不能修改。

現在我們對id=1的資料行排他查詢,這裡會使用begin開啟事務,而不會看見我關閉事務,這樣做是用來測試,因為提交事務或回滾事務就會釋放鎖。

事務一 事務二
步驟1 -- 開啟事務、加共享鎖,start transaction; -- 開啟事務、加共享鎖,鎖住id<3的所有行start transaction;select * from s1 where id < 3 lock in share mode;
步驟2 -- 加排他鎖,會阻塞在原地select * from s1 where id = 1 for update;-- 加共享鎖,可以查出結果,不會阻塞在原地select * from s1 where id = 1 lock in share mode;-- 不加鎖,必然也可以查出結果,不會阻塞在原地select name from s1 where id = 1;
步驟3 -- 提交一下事務,不要影響下一次實驗commit; -- 提交一下事務,不要影響下一次實驗commit;

注:在其他事務裡也只能加共享鎖或不加鎖。

2、排它鎖

排他鎖又稱為寫鎖,簡稱X鎖,顧名思義,排他鎖就是不能與其他鎖並存,如一個事務獲取了一個數據行的排他鎖,其他事務就不能再獲取該行的其他鎖,包括共享鎖和排他鎖,但是獲取排他鎖的事務是可以對資料就行讀取和修改。

事務一 事務二
步驟1 -- 開啟事務 start transaction; -- 開啟事務start transaction;
步驟2 -- 加排他鎖,鎖住id<3的所有行select * from s1 where id < 3 for update;
步驟3 -- 阻塞在原地select * from s1 where id = 1 for update;-- 阻塞在原地select * from s1 where id = 1 lock in share mode;-- 我們看到開了排他鎖查詢和共享鎖查詢都會處於阻塞狀態-- 因為id=1的資料已經被加上了排他鎖,此處阻塞是等待排他鎖釋放。
步驟4 -- ctrl+c終止步驟3的阻塞狀態-- 注意:-- 下述實驗遇到阻塞都可以用採用ctrl+c的方式結束,或者等待鎖超時
步驟5 -- 如果我們直接使用以下查詢,即便id<3的行都被事務二鎖住了-- 但此處仍可以檢視到資料-- 證明普通select查詢沒有任何鎖機制select name from s1 where id = 1;
步驟6 -- 提交一下事務,不要影響下一次實驗commit; -- 提交一下事務,不要影響下一次實驗commit;

3、死鎖

兩個事務都持有對方需要的鎖,並且在等待對方釋放,並且雙方都不會釋放自己的鎖。

所謂死鎖:是指兩個或兩個以上的程序在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的程序稱為死鎖程序。表級鎖不會產生死鎖.所以解決死鎖主要還是針對於最常用的InnoDB。

事務一 事務二
步驟1 -- 開啟事務 start transaction; -- 開啟事務start transaction;
步驟2 -- 查詢id=1這條資料時,增加排它鎖select * from s1 where id = 1 for update;
步驟3 -- 在事務二中刪除一條資料,增加了排它鎖delete from s1 where id = 5;
步驟4 -- 修改事務二中的資料,發現出現了阻塞,這是因為id=5這條資料在步驟3中已經被鎖定了update s1 set name = "ShanHe" where id = 5;
步驟5 -- 在事務二中刪除事務一中之前增加的鎖的資料delete from s1 where id = 1;
步驟6 -- 隨即,事務一中的命令出現死鎖錯誤ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
步驟7 -- 提交一下事務,不要影響下一次實驗commit; -- 提交一下事務,不要影響下一次實驗commit;

為何會出現死鎖?

因為事務二中,步驟4的update語句是想獲取互斥鎖,會阻塞在原地,需要等待事務一先釋放共享鎖。 而事務一執行下述了下述delete語句同樣是想獲取互斥鎖, 同樣需要等事務二先釋放共享鎖,至此雙方互相鎖死。事務一在丟擲死鎖異常之後,會被強行終止,只剩事務二自己,這個時候事務二就可以得到他所需要的鎖, 於是事務二的sql不存在鎖爭搶問題,會立即執行成功。

五、Innodb儲存引擎的鎖機制

MyISAM和MEMORY採用表級鎖(table-level locking)。

BDB採用頁級鎖(page-level locking)或表級鎖,預設為頁級鎖。

InnoDB支援行級鎖(row-level locking)和表級鎖,預設為行級鎖(偏向於寫)。

InnoDB的鎖定模式實際上可以分為四種:共享鎖(S),排他鎖(X),意向共享鎖(IS)和意向排他鎖(IX),我們可以通過以下表格來總結上面這四種所的共存邏輯關係:

如果一個事務請求的鎖模式與當前的鎖相容,InnoDB就將請求的鎖授予該事務;反之,如果兩者不相容,該事務就要等待鎖釋放。

1、行級鎖與表級鎖的使用區分

MyISAM 操作資料都是使用表級鎖,MyISAM總是一次性獲得所需的全部鎖,要麼全部滿足,要麼全部等待。所以不會產生死鎖,但是由於每操作一條記錄就要鎖定整個表,導致效能較低,併發不高。

InnoDB 與 MyISAM 的最大不同有兩點:一是 InnoDB 支援事務;二是 InnoDB 採用了行級鎖。也就是你需要修改哪行,就可以只鎖定哪行。

在Mysql中,行級鎖並不是直接鎖記錄,而是鎖索引。InnoDB 行鎖是通過給索引項加鎖實現的,而索引分為主鍵索引和非主鍵索引兩種:

    1. 如果一條sql 語句操作了主鍵索引,Mysql 就會鎖定這條語句命中的主鍵索引(或稱聚簇索引)。
    2. 如果一條語句操作了非主鍵索引(或稱輔助索引),MySQL會先鎖定該非主鍵索引,再鎖定相關的主鍵索引。
    3. 如果沒有索引,InnoDB 會通過隱藏的主鍵索引來對記錄加鎖。也就是說:如果不通過索引條件檢索資料,那麼InnoDB將對錶中所有資料加鎖,實際效果跟表級鎖一樣。

在實際應用中,要特別注意InnoDB行鎖的這一特性,不然的話,可能導致大量的鎖衝突,從而影響併發效能。

    1. 在不通過索引條件查詢的時候,InnoDB 的效果就相當於表鎖
    2. 當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,另外,無論是使用主鍵索引、唯一索引或普通索引,InnoDB 都會使用行鎖來對資料加鎖。
    3. 由於 MySQL 的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以即便你的sql語句訪問的是不同的記錄行,但如果命中的是相同的被鎖住的索引鍵,也還是會出現鎖衝突的。
    4. 即便在條件中使用了索引欄位,但是否使用索引來檢索資料是由 MySQL 通過判斷不同 執行計劃的代價來決定的,如果 MySQL 認為全表掃 效率更高,比如對一些很小的表,它 就不會使用索引,這種情況下 InnoDB 將鎖住所有行,相當於表鎖。因此,在分析鎖衝突時, 別忘了檢查 SQL 的執行計劃,以確認是否真正使用了索引。

1、驗證未命中索引引發表鎖

事務一 事務二
步驟1 -- 開啟事務 start transaction; -- 開啟事務start transaction;
步驟2 -- 查詢時未命中索引,從而引發表鎖select * from s1 where email like "%xxx" for update;
步驟3 -- 在事務二中查詢資料阻塞select * from s1 where id = 2 for update ;-- 在事務二中查詢資料阻塞select * from s1 where id = 3 for update ;-- 在事務二中查詢資料阻塞select * from s1 where id = 4 for update ;
步驟4 -- 提交一下事務,不要影響下一次實驗commit; -- 提交一下事務,不要影響下一次實驗commit;

2、驗證命中索引則鎖行

事務一 事務二
步驟1 -- 開啟事務 start transaction; -- 開啟事務start transaction;
步驟2 -- 查詢時未命中索引,從而引發表鎖select * from s1 where email like "%xxx" for update;
步驟3 -- 在事務二中查詢資料正常select * from s1 where id = 2 for update ;-- 在事務二中查詢資料正常select * from s1 where id = 3 for update ;-- 在事務二中查詢資料正常select * from s1 where id = 4 for update ;
步驟4 -- 提交一下事務,不要影響下一次實驗commit; -- 提交一下事務,不要影響下一次實驗commit;

2、三種行鎖的演算法

InnoDB有三種行鎖的演算法,都屬於排他鎖:

    1. Record Lock:單個行記錄上的鎖。
    2. Gap Lock:間隙鎖,鎖定一個範圍,但不包括記錄本身。GAP鎖的目的,是為了防止同一事務的兩次當前讀,出現幻讀的情況。
當我們用範圍條件而不是相等條件檢索資料,並請求共享或排他鎖時,InnoDB會給符合條件的已有資料記錄的索引項加鎖;
對於鍵值在條件範圍內但並不存在的記錄,叫做“間隙(GAP)”,InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖(Next-Key鎖)。 

# 例如
例:假如employee表中只有101條記錄,其depid的值分別是 1,2,...,100,101,下面的SQL:
mysql> select * from emp where depid > 100 for update;是一個範圍條件的檢索,並且命中了索引,InnoDB不僅會對符合條件的empid值為101的記錄加鎖,也會對empid大於101(這些記錄並不存在)的“間隙”加鎖。
    1. Next-Key Lock:等於Record Lock結合Gap Lock,也就說Next-Key Lock既鎖定記錄本身也鎖定一個範圍,特別需要注意的是,InnoDB儲存引擎還會對輔助索引下一個鍵值加上gap lock。

對於行查詢,innodb採用的都是Next-Key Lock,主要目的是解決幻讀的問題,以滿足相關隔離級別以及恢復和複製的需要。

# 準備資料
mysql> create table t1(id int,key idx_id(id))engine=innodb;
Query OK, 0 rows affected (0.03 sec)

mysql> insert t1 values (1),(5),(7),(11);
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> explain select * from t1 where id=7 for update\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
   partitions: NULL
         type: ref
possible_keys: idx_id
          key: idx_id
      key_len: 5
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.00 sec) 5-7 7-11    5 - 11 

實驗測試

事務一 事務二
步驟1 -- 開啟事務start transaction; -- 開啟事務start transaction;
步驟2 -- 加排他鎖select * from t1 where id=7 for update;-- 須知-- 1、上述語句命中了索引,所以加的是行鎖-- 2、InnoDB對於行的查詢都是採用了Next-Key Lock的演算法,鎖定的不是單個值,而是一個範圍(GAP)表記錄的索引值為1,5,7,11,其記錄的GAP區間如下:(-∞,1],(1,5],(5,7],(7,11],(11,+∞)因為記錄行預設就是按照主鍵自增的,所以是一個左開右閉的區間其中上述查詢條件id=7處於區間(5,7]中,所以Next-Key lock會鎖定該區間的記錄,但是還沒完-- 3、InnoDB儲存引擎還會對輔助索引下一個鍵值加上gap lock。區間(5,7]的下一個Gap是(7,11],所以(7,11]也會被鎖定綜上所述,最終確定5-11之間的值都會被鎖定
步驟3 -- 下述sql全都會阻塞在原地insert t1 values(5);insert t1 values(6);insert t1 values(7);insert t1 values(8);insert t1 values(9);insert t1 values(10);-- 下述等sql均不會阻塞insert t1 values(11); insert t1 values(1); insert t1 values(2);insert t1 values(3);insert t1 values(4);
步驟4 -- 提交一下事務,不要影響下一次實驗commit; -- 提交一下事務,不要影響下一次實驗commit;

插入超時失敗後,會怎麼樣?
超時時間的引數:innodb_lock_wait_timeout ,預設是50秒。
超時是否回滾引數:innodb_rollback_on_timeout 預設是OFF。

3、什麼時候使用表鎖

絕大部分情況使用行鎖,但在個別特殊事務中,也可以考慮使用表鎖

  1. 事務需要更新大部分資料,表又較大

若使用預設的行鎖,不僅該事務執行效率低(因為需要對較多行加鎖,加鎖是需要耗時的); 而且可能造成其他事務長時間鎖等待和鎖衝突; 這種情況下可以考慮使用表鎖來提高該事務的執行速度。

  1. 事務涉及多個表,較複雜,很可能引起死鎖,造成大量事務回滾

這種情況也可以考慮一次性鎖定事務涉及的表,從而避免死鎖、減少資料庫因事務回滾帶來的開銷當然,應用中這兩種事務不能太多,否則,就應該考慮使用MyISAM。

4、行鎖優化建議

通過檢查InnoDB_row_lock狀態變數來分析系統上的行鎖的爭奪情況,在著手根據狀態量來分析改善。

mysql> show status like 'innodb_row_lock%';
+-------------------------------+--------+
| Variable_name                 | Value  |
+-------------------------------+--------+
| Innodb_row_lock_current_waits | 0      |
| Innodb_row_lock_time          | 114092 |
| Innodb_row_lock_time_avg      | 7606   |
| Innodb_row_lock_time_max      | 50683  |
| Innodb_row_lock_waits         | 15     |
+-------------------------------+--------+
5 rows in set (0.00 sec)
  • 儘可能讓所有資料檢索都通過索引來完成, 從而避免無索引行鎖升級為表鎖
  • 合理設計索引,儘量縮小鎖的範圍
  • 儘可能減少檢索條件,避免間隙鎖
  • 儘量控制事務大小,減少鎖定資源量和時間長度
  • 儘可能使用低級別事務隔離

六、樂觀鎖與悲觀鎖

資料庫管理系統(DBMS)中的併發控制的任務是確保在多個事務同時存取資料庫中同一資料時不破壞事務的隔離性和統一性以及資料庫的統一性。

樂觀併發控制(樂觀鎖)和悲觀併發控制(悲觀鎖)是併發控制主要採用的技術手段。

無論是悲觀鎖還是樂觀鎖,都是人們定義出來的概念,可以認為是一種思想。其實不僅僅是關係型資料庫系統中有樂觀鎖和悲觀鎖的概念,像memcache、hibernate、tair等都有類似的概念。

針對於不同的業務場景,應該選用不同的併發控制方式。所以,不要把樂觀併發控制和悲觀併發控制狹義的理解為DBMS中的概念,更不要把他們和資料中提供的鎖機制(行鎖、表鎖、排他鎖、共享鎖)混為一談。其實,在DBMS中,悲觀鎖正是利用資料庫本身提供的鎖機制來實現的。

下面來分別學習一下悲觀鎖和樂觀鎖。

1、悲觀鎖

當我們要對一個數據庫中的一條資料進行修改的時候,為了避免同時被其他人修改,最好的辦法就是直接對該資料進行加鎖以防止併發。

這種藉助資料庫鎖機制在修改資料之前先鎖定,再修改的方式被稱之為悲觀併發控制(又名“悲觀鎖”,Pessimistic Concurrency Control,縮寫“PCC”)。

案例:假設商品表中有一個欄位quantity表示當前該商品的庫存量。假設有一件Dulex套套,其id為100,quantity=8個;如果不使用鎖,那麼操作方法:

//step1: 查出商品狀態
select quantity from items where id=100 for update;
//step2: 根據商品資訊生成訂單
insert into orders(id,item_id) values(null,100);
//step3: 修改商品的庫存
update Items set quantity=quantity-2 where id=100;

// 樂觀鎖
update items set quantity=quantity-2 where id = 100 and quantity-2 >= 0;
//step2: 根據商品資訊生成訂單
insert into orders(id,item_id) values(null,100);

select...for update是MySQL提供的實現悲觀鎖的方式。此時在items表中,id為100的那條資料就被我們鎖定了,其它的要執行select quantity from items where id=100 for update的事務必須等本次事務提交之後才能執行。這樣我們可以保證當前的資料不會被其它事務修改。

悲觀併發控制主要用於資料爭用激烈的環境,以及發生併發衝突時使用鎖保護資料的成本要低於回滾事務的成本的環境中。

  • 優點:

    • 悲觀併發控制實際上是“先取鎖再訪問”的保守策略,為資料處理的安全提供了保證。
  • 缺點:

    • 在效率方面,處理加鎖的機制會讓資料庫產生額外的開銷,還有增加產生死鎖的機會;
    • 在只讀型事務處理中由於不會產生衝突,也沒必要使用鎖,這樣做只能增加系統負載;還有會降低了並行性,一個事務如果鎖定了某行資料,其他事務就必須等待該事務處理完才可以處理那行數

2、樂觀鎖

樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認為資料一般情況下不會造成衝突,所以在資料進行提交更新的時候,才會正式對資料的衝突與否進行檢測,如果發現衝突了,則讓返回使用者錯誤的資訊,讓使用者決定如何去做。

相對於悲觀鎖,在對資料庫進行處理的時候,樂觀鎖並不會使用資料庫提供的鎖機制。一般的實現樂觀鎖的方式就是記錄資料版本。

案例

在使用mysql資料庫儲存資料的前提下,有一個搶任務系統,一個任務只能分配給n個人,如果有高併發請求,如何保證資料完整性?

步驟

在不考慮到資料是否完整的情況下,我們一般只會按照以下思維開發:

    1. 使用者請求搶任務介面
    2. 讀取資料庫剩餘數量
    3. 如果大於0,剩餘數量減1,更新資料庫剩餘數量(update task set count=count-1 where id=‘任務id’)
    4. 返回資料

以上SQL其實還是有一定的問題的,就是一旦發上高併發的時候,就只有一個執行緒可以修改成功,那麼就會存在大量的失敗。

對於像淘寶這樣的電商網站,高併發是常有的事,總讓使用者感知到失敗顯然是不合理的。所以,還是要想辦法減少樂觀鎖的粒度的。

有一條比較好的建議,可以減小樂觀鎖力度,最大程度的提升吞吐率,提高併發能力!如下:

//修改商品庫存 
update task set count=count-1 where id=‘任務id’ and count=‘讀取到的剩餘數量’ and count-1 >= 0;

以上SQL語句中,通過count-1>0的方式進行樂觀鎖控制,商品個數count至少要有1件才可以。

以上update語句,在執行過程中,會在一次原子操作中自己查詢一遍count的值,並將其扣減掉1。

沒錯!你參加過的天貓、淘寶秒殺、聚划算,跑的就是上述這條SQL,通過挑選樂觀鎖,可以減小鎖力度,從而提升吞吐~

優點與不足

樂觀併發控制相信事務之間的資料競爭(data race)的概率是比較小的,因此儘可能直接做下去,直到提交的時候才去鎖定,所以不會產生死鎖。

如何選擇

在樂觀鎖與悲觀鎖的選擇上面,主要看下兩者的區別以及適用場景就可以了。

  1. 樂觀鎖並未真正加鎖,效率高。一旦鎖的粒度掌握不好,更新失敗的概率就會比較高,容易發生業務失敗
  2. 悲觀鎖依賴資料庫鎖,效率低。更新失敗的概率比較低。

隨著網際網路三高架構(高併發、高效能、高可用)的提出,悲觀鎖已經越來越少的被使用到生產環境中了,尤其是併發量比較大的業務場景