全局鎖與表鎖
鎖的概念
鎖用於協調多個客戶端對同一數據的併發訪問,保證併發訪問時數據的有效性和一致性。
MySQL的鎖分爲全局鎖、表鎖和行鎖。
數據準備
創建一個表格,對後續鎖的使用演示做準備。
CREATE TABLE t (
id int(11) NOT NULL AUTO_INCREMENT,
a int(11) NOT NULL,
b int(11) NOT NULL,
PRIMARY KEY (id),
KEY idx_a (a)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
insert into t(a,b) values(1,1);
創建表格t,插入一行數據。
全局鎖
全局鎖會關閉所有打開的表並使用全局鎖鎖定數據庫中全部表格。所有表都處於只讀狀態,任何數據、字段的更新都會被阻塞。
一般在數據庫備份過程中會使用到全局鎖,如使用mysqldump
命令。整個備份過程中,庫都是隻讀的。不過該命令存在參數--single-transaction
,可在事務中創建一致性快照,增加該參數後在數據備份過程中可以對數據進行更新。
對庫中所有表進行鎖定和解鎖的命令如下,
# 全局讀鎖鎖定
flush tables with read lock;
# 全局讀鎖解鎖
unlock tables;
全局鎖實驗,
session1 | session2 |
---|---|
flush tables with read lock; | |
select * from t; (正常返回結果) | select * from t; (正常返回結果) |
insert into t(a, b) values(2, 2); (報錯) | insert into t(a, b) values(2, 2); (等待) |
unlock tables; | 解鎖後上方insert語句執行成功 |
當全局讀鎖上鎖後,所有表變爲只讀狀態,數據更新或字段更新都會被阻塞。
表鎖和元數據鎖
1)表鎖
表鎖使用場景及分類
表鎖使用場景:
- 事務中需要對某張大表內的大部分或全部數據進行更新。此時如果使用行鎖,會引發低效、衝突等情況,而使用表鎖可以提升性能。
- 事務涉及多個表,比較複雜且容易導致死鎖,考慮使用表鎖能夠避免死鎖。
表鎖又分爲表讀鎖和表寫鎖,對二者的使用進行說明,
表鎖上鎖命令
對之前構建的表t上表讀鎖和表寫鎖,
# 表讀鎖
lock tables t read;
# 表寫鎖
lock tables t write;
表讀鎖使用
session1 | session2 |
---|---|
lock table t read; | |
select id, a, b from t limit 1; (正常返回結果) | select id, a, b from t limit 1; (正常返回結果) |
insert into t(a, b) values(3, 4); (報錯) | insert into t(a, b) values(3, 4); (阻塞) |
unlock tables; | 上方阻塞語句執行成功 |
表寫鎖使用
session1 | session2 |
---|---|
lock table t write; | |
select id, a, b from t limit 1; (正常返回結果) | select id, a, b from t limit 1; (阻塞) |
unlock tables; | 上方阻塞語句執行成功 |
lock table t write; | |
delete from t limit 1; (正常刪除記錄) | delete from t limit 1; (阻塞) |
unlock tables; | 上方阻塞語句執行成功 |
表鎖使用總結
鎖類型 | 當前session讀 | 其餘session讀 | 當前session寫 | 其餘session寫 |
---|---|---|---|---|
表讀鎖 | 可讀 | 可讀 | 不可寫,報錯 | 不可寫,阻塞 |
表寫鎖 | 可讀 | 不可讀,阻塞 | 可寫 | 不可寫,阻塞 |
2)元數據鎖(MDL)
元數據鎖相關概念
MySQL中DDL(數據定義語言)不屬於事務範疇,當DDL與事務併發時會出現事務特性被破壞、binlog順序錯亂等bug。從MySQL 5.5.3版本後引入元數據鎖解決事務與DDL並行時數據不一致的問題。
概念辨析
- DML
數據操縱語言,用於查詢和修改數據,如insert新增記錄、update更新原有記錄、delete刪除原有記錄和select:查詢 - DDL
用於定義數據庫的結構,比如創建,修改刪除數據庫對象,create table … 創建表、drop table… 刪除表、create index…創建索引、drop index …刪除索引和alter table…更改表結構,增加,刪除列,修改列的數據類型,長度等;
每執行一條DML、DDL語句時都會申請MDL鎖,DML操作需要MDL讀鎖,DDL操作需要MDL寫鎖(MDL加鎖過程是系統自動控制,無法直接干預,讀讀共享,讀寫互斥,寫寫互斥)
MDL的存在可能導致長時間所等待,如果該表是查詢頻繁的表,很可能算時間內數據庫連接數就被打滿。
明確DDL操作,
操作 | 說明 |
---|---|
create database 庫名; | 創建數據庫 |
drop database 庫名; | 刪除數據庫 |
show databases; | 查看MySQL下所有的庫 |
desc 表名; | 查看錶中的字段 |
rename table 舊錶名 to 新表名; | 對已經存在的表進行重命名 |
alter table 表名 add 字段名 數據類型; | 向已存在的表中添加字段信息 |
alter table 表名 drop 被刪除的字段名; | 刪除指定表中的指定字段 |
alter table 表名 change 舊字段名 新字段名 新字段類型; | 對錶中字段進行重命名 |
alter table 表名 engine=新引擎名; | 更改表的存儲引擎 |
alter table 表名 drop foreign key 外鍵名; | 刪除外鍵約束 |
元數據鎖阻塞示例
MDL引發阻塞演示,假設四個語句先後執行,session A的語句執行過程需要一段時間,
爲什麼C等待拿鎖之後,D也會阻塞?其實這裏並沒有解釋清楚。因爲如果按併發理解的話,C,D應當是同等級,都有可能拿到鎖的。但C寫鎖與sessionA的讀鎖互斥,D讀鎖sessionA與不互斥,這樣的話就跟上圖所述相悖了。
申請MDL鎖的操作會形成一個隊列,隊列中寫鎖獲取優先級高於讀鎖。一旦出現寫鎖等待,不但當前操作會被阻塞,同時還會阻塞後續該表的所有操作。事務一旦申請到MDL鎖後,直到事務執行完纔會將鎖釋放。
這樣就能解釋通爲什麼session C被阻塞後,session D也運行不了的原因了。
online DDL插隊現象
結合上面的表格進行試驗,實際操作過程中會出現這樣的現象,
這個問題就要涉及到online DDL。由於DDL讀寫互斥,嚴重影響性能,於是MySQL推出了全新的online DDL概念,即通過,
- 拿MDL寫鎖
- 降級成MDL讀鎖
- 真正做DDL
- 升級成MDL寫鎖
- 釋放MDL鎖
具體圖示如下,
該部分內容參考博文。