33 SQL 鎖
1. 定義
解釋:一把
鎖
對應一扇門,獲得鎖的可以進門,否則只能在門外等待。
2. 前言
本小節,我們將一起學習 SQL 中的鎖
。
在一些併發場景中,會涉及到一些資料競爭問題。如 A、B 二人同時要修改同一條記錄,如果二人可以對其同時修改,那麼很大的概率上,資料會起衝突,為了保證資料的安全性和正確性,SQL 引入了鎖
。
本小節測試資料如下,請先在資料庫中執行:
DROP TABLE IF EXISTS imooc_user;
CREATE TABLE imooc_user
(
id int PRIMARY KEY,
username varchar(20),
age int
);
INSERT INTO imooc_user(id,username,age)
VALUES (1,'peter',18),(2,'pedro',24),(3,'jerry',22),(4,'mike',18),(5,'tom',20);
3. 鎖的分類
鎖的種類非常多,專業名詞數不勝數,我們無需將其所有都記住,在本小節我們只瞭解其常用且提及最廣的部分。
從鎖的粒度
上,我們可以將其大致的分為如下幾類:
名稱 | 描述 | 說明 |
---|---|---|
庫鎖 | 鎖定某個資料庫 | 粒度最大,若非特殊情況(資料庫備份),切勿使用。 |
表鎖 | 鎖定某張資料表 | 粒度也比較大,直接涉及一張表,若非特殊情況,也勿使用。 |
頁鎖 | 鎖定某張資料頁 | SQL Server 特有的鎖,會鎖定資料頁,資料表中的資料是按頁組織的。 |
行鎖 | 鎖定某一行記錄 | 粒度最小,只鎖定一條記錄,推薦使用。 |
從資料庫系統管理
角度來看,可以把鎖分為如下兩大類:
名稱 | 描述 | 說明 |
---|---|---|
共享鎖 | 其他人可以讀取,但不能修改 | 也被稱為讀鎖 |
排他鎖 | 其他人不能讀取,也不能修改 | 也被稱為寫鎖 |
鎖的種類還有很多,實現方式也多姿多彩,如果你感興趣,可以查閱一下相關的資料。
我們分別從粒度和管理兩個角度上對鎖進行了分類。
在粒度上,不同資料庫,甚至不同引擎對鎖的粒度支援都是不同的,如 MySQL 的 InnoDB 引擎支援行鎖、表鎖和庫鎖,而 MyISAM 引擎只能支援到表鎖。對於頁鎖,只有 SQL Server 支援,而不同資料庫也有類似間隙鎖的實現,它的功能與頁鎖差不多。
在管理上,鎖根據資料是否共享來分類,對於讀多寫少的場景,共享鎖幾乎是併發的標配,而一旦涉及資料修改,鎖就必須獨佔了。
4. 實踐
下面,我們以幾個例子來熟悉一下鎖的使用。
4.1 例1、鎖住 imooc_user 表
在 SQL 中,你可以通過如下語句鎖住某一張表:
LOCK TABLE [table_name] [READ|WRITE];
其中table_name
表示資料表名稱,[READ|WRITE]
表示可以任選READ(讀鎖)
或WRITE(寫鎖)
中的一種。
當需要解鎖時,只需如下語句:
UNLOCK TABLE;
請書寫 SQL 語句,鎖住imooc_user
表,但其他人可讀。
分析:
題幹中指出,他人可讀,因此鎖為讀鎖,通過 LOCK TABLE 鎖住該表即可。
語句:
整理可得語句如下:
LOCK TABLE imooc_user READ;
鎖住後,其他人仍然能夠讀取 imooc_user 表的資料,如下:
# select * from imooc_user;
+----+----------+
| id | username |
+----+----------+
| 1 | peter |
| 2 | pedro |
| 3 | jerry |
| 4 | mike |
| 5 | tom |
+----+----------+
測試完畢後,我們一定記得解鎖:
UNLOCK TABLE;
4.2 例2、鎖住 pedro 使用者
對於某一條記錄(某一行),SQL 提交如下方式來加讀鎖:
SELECT * FROM [table_name] WHERE [condition] LOCK IN SHARE MODE;
其中table_name
表示資料表名稱,condition
表示過濾條件。
如果你要獨佔這一行的資料,可以這樣加上寫鎖:
SELECT * FROM [table_name] WHERE [condition] FOR UPDATE;
注意: 在測試時,你必須在一個
事務
裡面進行行鎖,否則查詢直接退回,鎖的時間極短。
請書寫 SQL 語句,鎖住 imooc_user 表中使用者pedro
,只允許別人讀,不允許別人寫。
分析:
pedro 使用者是表中的一條記錄,因此通過 SELECT … LOCK … 的方式加上行讀鎖,為了方便測試我們以一個事務的方式來操作鎖。
語句:
整理可得語句如下:
BEGIN;
SELECT * FROM imooc_user WHERE id = 1 LOCK IN SHARE MODE;
鎖住該行後,其他使用者可以讀取它卻不能修改它,直到釋放鎖才能修改,如下:
COMMIT;
有時候,我們也需要更加霸道地鎖住 pedro,即不讓人寫,也不讓人讀,這個時候就可以使用寫鎖。
BEGIN;
SELECT * FROM imooc_user WHERE id = 1 FOR UPDATE;
操作完畢後,我們一定記得提交事務以釋放鎖。
COMMIT;
5. 個人經驗
- 鎖與事務都是面試必備,且二者往往都是彼此關聯。
- 鎖的內容浩瀚如海,本小節以粒度和管理兩個視角,簡單地介紹了鎖,在實戰部分,我們還會接著討論它。