MySQL鎖是用于管理數據庫中的并發操作的一種機制,它可以確保數據的一致性和完整性。
按范圍劃分:包括全局鎖、表級鎖、頁級鎖和行級鎖。
按類型劃分:包括間隙鎖、臨鍵鎖和記錄鎖。
按級別劃分:包括共享鎖(S鎖)和排它鎖(X鎖),以及意向鎖。
全局鎖
全局鎖是MySQL中鎖的一種粒度,它影響的是整個數據庫實例。當施加全局鎖時,數據庫中的所有表都無法進行寫操作,即所有的數據更新語句(增刪改)和數據定義語句(如創建、修改、刪除表結構等)都會被阻塞。
典型的使用場景是在執行全庫邏輯備份時,通過施加全局鎖來確保數據的一致性,從而防止備份過程中的數據不一致問題。這對于保證備份數據的完整性和準確性非常關鍵。
-- 施加全局讀鎖(共享鎖)
FLUSH TABLES WITH READ LOCK;-- 執行備份操作...-- 釋放全局讀鎖
UNLOCK TABLES;
表級鎖
每次操作鎖住整張表。開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,并發度最低;一般用在整表數據遷移的場景。
手動增加表鎖:
lock table 表名稱 read(write),表名稱2 read(write);
例如lock tables t1 read, t2 write; 命令,則其他線程寫 t1、讀寫 t2 的語句都會被阻塞。同時,線程 A 在執行 unlock tables 之前,也只能執行讀 t1、讀寫 t2 的操作。連寫 t1 都不允許,自然也不能在unlock tables之前訪問其他表。
查看表上加過的鎖:
show open tables;
刪除表鎖:
unlock tables;
頁級鎖
頁級鎖的鎖定粒度和加鎖時間介于表級鎖和行級鎖之間,適用于對相鄰一組記錄的操作。
以下是頁級鎖的示例:
-- 開啟事務
START TRANSACTION;-- 執行查詢操作,并使用LOCK IN SHARE MODE對結果集進行共享鎖定
SELECT * FROM table_name WHERE condition LOCK IN SHARE MODE;-- 執行更新操作,并使用FOR UPDATE對結果集進行排他鎖定
UPDATE table_name SET column1 = value1, column2 = value2 WHERE condition FOR UPDATE;-- 提交事務
COMMIT;
SELECT … LOCK IN SHARE MODE語句會將查詢結果集進行共享鎖定,允許其他事務讀取該結果集,但不允許對其進行修改。而UPDATE … FOR UPDATE語句則會將更新的結果集進行排他鎖定,防止其他事務對該結果集進行讀取或修改。
行鎖
每次操作鎖住一行數據。鎖定粒度最小,發生鎖沖突的概率最低,并發度最高,但是加鎖慢、開銷大,容易發生死鎖現象。
InnoDB與MYISAM的最大不同有兩點:
- InnoDB支持事務
- InnoDB支持行級鎖
InnoDB在執行查詢語句SELECT時(非串行隔離級別),不會加鎖。但是update、insert、delete操作會加行鎖。
共享鎖和排他鎖
-
讀鎖/共享鎖(Shared Lock,S鎖):針對同一份數據,多個事務的讀操作可以同時進行而不會互相影響,相互不阻塞的。
-
寫鎖/排它鎖(Exclusive Lock,X鎖):允許事務對某些數據進行刪除或更新。如果當前操作還沒完成,其他事務的S和X鎖是會被阻塞的,確保在多個事務中,對同一資源,只有一個事務能寫入,并防止其他用戶讀取正在寫入的資源。
S鎖 | X鎖 | |
---|---|---|
S鎖 | 兼容 | 不兼容 |
X鎖 | 不兼容 | 不兼容 |
共享鎖稱為讀鎖,但不是讀一定獲取共享鎖。正常情況下,select某一條記錄時,只需要獲取該記錄的共享鎖。但是,在有些情況下可能select記錄時就獲取記錄的排他鎖,來禁止別的事務來讀取該記錄,為此,MySQL提供了兩種特殊的select語句:
對讀取的記錄加S鎖:
SELECT... LOCK IN SHARE MODE; SELECT ... FOR SHARE [NOWAIT|SKIP LOCKED];-- 8.0特性
——NOWAIT表示不等待直接報錯,
——SKIP LOCKED表示立即返回,但返回的結果不包含被鎖定的行
此時允許其他事務讀取該記錄(給該記錄加S鎖),但是不允許其他事物給該記錄加X鎖,需要阻塞等待當前事務提交后獲取鎖。
對讀寫的記錄加X鎖:
select * from user where id=1 for update;
如果當前事務中有該語句,會給記錄增加X鎖,不允許其他事務獲取該記錄的S鎖和X鎖。
意向鎖
意向鎖的作用就是加快表鎖的檢查過程。
意向鎖是由存儲引擎自己維護的,用戶無法手動獲取,在為數據行加共享/排他鎖之前,InooDB會先獲取該數據所在表的對應意向鎖。
- 意向共享鎖(intention shared lock,IS)
在使用InnoDB引擎的表里對某些記錄加上「共享鎖」之前,需要先在表級別加上一個「意向共享鎖」:
——事務要獲取某些行的S鎖,必須先獲得表的 IS 鎖。
SFLFCT column FROM table ... LOCK IN SHARE MODE;
- 意向排他鎖(intention exclusive lock,IX)
在使用InnoDB引擎的表里對某些紀錄加上「獨占鎖」之前,需要先在表級別加上一個「意向獨占鎖」:
——事務要獲取某些行的X鎖,必須先獲得表的IX鎖。
SELECT column FROM table ... FOR UPDATE;
間隙鎖
間隙鎖的作用是防止其他事務在當前事務未提交的情況下插入新的記錄,從而避免幻讀現象的發生。當一個事務執行了范圍查詢操作時,MySQL會自動為該范圍加上間隙鎖,以防止其他事務在該范圍內插入新的記錄。
需要注意的是,間隙鎖的加鎖范圍是左開右閉區間,即不包括范圍的左右端點。例如,如果一個事務執行了以下范圍查詢操作:
SELECT * FROM table_name WHERE column > 10 AND column < 20;
那么MySQL會為該范圍加上間隙鎖,鎖定從10到20之間的所有記錄,但不包括10和20這兩個值本身。
臨鍵鎖(Next-key Locks)
臨鍵鎖,是記錄鎖與間隙鎖的組合,它的封鎖范圍,既包含索引記錄,又包含索引區間,是一個左開右閉區間。臨鍵鎖的主要目的,也是為了避免幻讀(Phantom Read)。如果把事務的隔離級別降級為RC,臨鍵鎖則也會失效。
每個數據行上的非唯一索引列上都會存在一把臨鍵鎖,當某個事務持有該數據行的臨鍵鎖時,會鎖住一段左開右閉區間的數據。需要強調的一點是,InnoDB 中行級鎖是基于索引實現的,臨鍵鎖只與非唯一索引列有關,在唯一索引列(包括主鍵列)上不存在臨鍵鎖。
Next-Key Locks是行鎖與間隙鎖的組合。像上面那個例子里的這個(3,20]的整個區間可以叫做臨鍵鎖。
記錄鎖
記錄鎖也叫行鎖,例如:
select * from emp where empno = 1 for update;
它會在 id=1 的記錄上加上記錄鎖,以阻止其他事務插入,更新,刪除 id=1 這一行。