數據庫鎖機制
一、數據庫的鎖機制
什么是鎖?為何要加入鎖機制?
鎖是計算機協調多個進程或線程並發訪問某一資源的機制,那為何要加入鎖機制呢?
因為在數據庫中,除了傳統的計算資源(如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 行鎖是通過給索引項加鎖實現的,而索引分為主鍵索引和非主鍵索引兩種:
-
如果一條sql 語句操作了主鍵索引,Mysql 就會鎖定這條語句命中的主鍵索引(或稱聚簇索引)。
-
如果一條語句操作了非主鍵索引(或稱輔助索引),MySQL會先鎖定該非主鍵索引,再鎖定相關的主鍵索引。
-
如果沒有索引,InnoDB 會通過隱藏的聚簇索引來對記錄加鎖。也就是說:如果不通過索引條件檢索數據,那么InnoDB將對表中所有數據加鎖,實際效果跟表級鎖一樣。
在實際應用中,要特別注意InnoDB行鎖的這一特性,不然的話,可能導致大量的鎖沖突,從而影響並發性能。
-
在不通過索引條件查詢的時候,InnoDB 的效果就相當於表鎖
-
當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,另外,無論是使用主鍵索引、唯一索引或普通索引,InnoDB 都會使用行鎖來對數據加鎖。
-
由於 MySQL 的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以即便你的sql語句訪問的是不同的記錄行,但如果命中的是相同的被鎖住的索引鍵,也還是會出現鎖沖突的。
-
即便在條件中使用了索引字段,但是否使用索引來檢索數據是由 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有三種行鎖的算法,都屬於排他鎖:
- Record Lock:單個行記錄上的鎖。
- 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(這些記錄並不存在)的“間隙”加鎖。
- 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)
實驗測試
事務一 | 事務二 | |
---|---|---|
步驟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、什么時候使用表鎖
絕大部分情況使用行鎖,但在個別特殊事務中,也可以考慮使用表鎖
- 事務需要更新大部分數據,表又較大
若使用默認的行鎖,不僅該事務執行效率低(因為需要對較多行加鎖,加鎖是需要耗時的); 而且可能造成其他事務長時間鎖等待和鎖沖突; 這種情況下可以考慮使用表鎖來提高該事務的執行速度。
- 事務涉及多個表,較復雜,很可能引起死鎖,造成大量事務回滾
這種情況也可以考慮一次性鎖定事務涉及的表,從而避免死鎖、減少數據庫因事務回滾帶來的開銷當然,應用中這兩種事務不能太多,否則,就應該考慮使用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;
select...for update是MySQL提供的實現悲觀鎖的方式。此時在items表中,id為100的那條數據就被我們鎖定了,其它的要執行select quantity from items where id=100 for update的事務必須等本次事務提交之后才能執行。這樣我們可以保證當前的數據不會被其它事務修改。
悲觀並發控制主要用於數據爭用激烈的環境,以及發生並發沖突時使用鎖保護數據的成本要低於回滾事務的成本的環境中。
-
優點:
-
- 悲觀並發控制實際上是“先取鎖再訪問”的保守策略,為數據處理的安全提供了保證。
-
缺點:
-
- 在效率方面,處理加鎖的機制會讓數據庫產生額外的開銷,還有增加產生死鎖的機會;
- 在只讀型事務處理中由於不會產生沖突,也沒必要使用鎖,這樣做只能增加系統負載;還有會降低了並行性,一個事務如果鎖定了某行數據,其他事務就必須等待該事務處理完才可以處理那行數
- 在效率方面,處理加鎖的機制會讓數據庫產生額外的開銷,還有增加產生死鎖的機會;
2、樂觀鎖
樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認為數據一般情況下不會造成沖突,所以在數據進行提交更新的時候,才會正式對數據的沖突與否進行檢測,如果發現沖突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。
相對於悲觀鎖,在對數據庫進行處理的時候,樂觀鎖並不會使用數據庫提供的鎖機制。一般的實現樂觀鎖的方式就是記錄數據版本。
案例
在使用mysql數據庫存儲數據的前提下,有一個搶任務系統,一個任務只能分配給n個人,如果有高並發請求,如何保證數據完整性?
步驟
在不考慮到數據是否完整的情況下,我們一般只會按照以下思維開發:
- 用戶請求搶任務接口
- 讀取數據庫剩余數量
- 如果大於0,剩余數量減1,更新數據庫剩余數量(update task set count=count-1 where id=‘任務id’)
- 返回數據
以上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)的概率是比較小的,因此盡可能直接做下去,直到提交的時候才去鎖定,所以不會產生任何鎖和死鎖。
如何選擇
在樂觀鎖與悲觀鎖的選擇上面,主要看下兩者的區別以及適用場景就可以了。
- 樂觀鎖並未真正加鎖,效率高。一旦鎖的粒度掌握不好,更新失敗的概率就會比較高,容易發生業務失敗
- 悲觀鎖依賴數據庫鎖,效率低。更新失敗的概率比較低。
隨着互聯網三高架構(高並發、高性能、高可用)的提出,悲觀鎖已經越來越少的被使用到生產環境中了,尤其是並發量比較大的業務場景。