
一.為什麼要加鎖?
資料庫鎖機制簡單來說,就是資料庫在多事務并發處理時,為了保證資料的一緻性和完整性,資料庫需要合理地控制資源的通路規則。鎖是一種資源,這個資源是和事務關聯在一起的,當某個事務擷取了鎖,在送出或復原之前,就一直持有該鎖。
二.鎖的分類
根據鎖類型劃分
共享鎖(讀鎖):其他事務可以讀,但不能寫。
排他鎖(寫鎖):其他事務不能讀取,也不能寫。
根據加鎖的範圍劃分
全局鎖、表鎖和行鎖三類。
全局鎖
全局鎖就是對整個資料庫執行個體加鎖。當你需要讓整個庫處于隻讀狀态的時候,可以使用這個指令,之後其他線程的以下語句會被阻塞:資料更新語句(資料的增删改)、資料定義語句(包括建表、修改表結構等)和更新類事務的送出語句。全局鎖的典型使用場景是,做全庫邏輯備份。
全局鎖的指令:
Flush tables with read lock
表鎖
MySQL中表級别的鎖有三種:表鎖、意向鎖和中繼資料鎖(meta data lock,MDL)。
表鎖的文法是lock tables…read/write,可以用unlock tables主動釋放鎖,也可以在用戶端斷開的時候自動釋放。
意向鎖主要分為意向共享鎖和意向互斥鎖。
意向共享鎖,事務想要給資料庫某些行加共享鎖,需要先給這張表加上意向共享鎖。
意向互斥鎖,事務想要給資料庫某些行加互斥鎖,需要先給這張表加上意向互斥鎖。
意向鎖主要是解決行鎖和表鎖的沖突問題。假設沒有意向鎖,事務A用行鎖鎖住其中一行,事務B申請表的互斥鎖,然後修改整個表,行鎖和表鎖就會發生沖突,事務B想知道事務A鎖住了哪一行,就需要周遊整張表,這是一個非常耗時的操作。引入意向鎖之後,事務A在加行鎖之前先給表加上意向鎖,這樣如果事務B來擷取表鎖,需要先判斷表上是否有意向鎖,如果有意向鎖則阻塞,等待事務A的鎖釋放。
MDL(metadata lock)是用來保護表的中繼資料資訊的,不需要顯式使用,在通路一個表的時候會被自動加上,MDL的作用是維護資料的一緻性,主要解決DML和DDL操作之間的一緻性問題。
行鎖
MySQL的行鎖是在引擎層由各個引擎自己實作的。但并不是所有的引擎都支援行鎖,比如MyISAM引擎就不支援行鎖。不支援行鎖意味着并發控制隻能使用表鎖,對于這種引擎的表,同一張表上任何時刻隻能有一個更新在執行,這就會影響到業務并發度。InnoDB是支援行鎖的,這也是MyISAM被InnoDB替代的重要原因之一。
行鎖又分為三種,單個行記錄的鎖(record lock)、間隙鎖(GAP Lock)、記錄鎖和間隙鎖的組合(next-key Lock)。
三.MDL鎖
為什麼要引入MDL鎖?
MySQL5.5引入了meta data lock,簡稱MDL鎖,屬于表鎖範疇。MDL的作用是,保證讀寫的正确性。你可以想象一下,如果一個查詢正在周遊一個表中的資料,而執行期間另一個線程對這個表結構做變更,增加了一列,那麼查詢線程拿到的結果跟表結構對不上,肯定是不行的。是以,當對一個表做增删改查操作的時候,加MDL讀鎖;當要對表做結構變更操作的時候,加MDL寫鎖。讀鎖之間不互斥,是以你可以有多個線程同時對一張表增删改查。讀寫鎖之間、寫鎖之間是互斥的,用來保證變更表結構操作的安全性。
中繼資料鎖的使用場景模拟
會話A:
mysql> begin;Query OK, 0 rows affected (0.00 sec)mysql> select count(1) from table_test;+----------+| count(1) |+----------+| 2439392 |+----------+1 row in set (7.46 sec)
會話B:
mysql> begin;Query OK, 0 rows affected (0.00 sec)mysql> alter table table_test add age int not null;
會話C:
mysql> show processlist;+----+------+-----------+--------+---------+------+---------------------------------+---------------------------------------------+| Id | User | Host | db | Command | Time | State | Info |+----+------+-----------+--------+---------+------+---------------------------------+---------------------------------------------+| 3 | root | localhost | gmdpdb | Sleep | 235 | | NULL || 6 | root | localhost | gmdpdb | Sleep | 138 | | NULL || 7 | root | localhost | gmdpdb | Query | 3 | Waiting for table metadata lock | alter table table_test add age int not null || 8 | root | localhost | NULL | Query | 0 | starting | show processlist |+----+------+-----------+--------+---------+------+---------------------------------+---------------------------------------------+4 rows in set (0.00 sec)
通過會話C可以看出會話B被阻塞,這是由于會話A拿到了table_test表的中繼資料讀鎖,會話B想申請table_test表的中繼資料寫鎖,由于讀寫鎖互斥,會話B需要等待會話A釋放中繼資料鎖才能執行。
中繼資料鎖可能帶來的問題
Session A
Session B
Session C
begin;
select * from t;
alter table t add age int;
select * from t;
我們可以看到session A會對表t加一個MDL讀鎖,之後session B要加MDL寫鎖會被blocked,因為session A的MDL讀鎖還沒有釋放,而session C要在表t上新申請MDL讀鎖的請求也會被session B阻塞。前面我們說了,所有對表的增删改查操作都需要先申請MDL讀鎖,就都被阻塞,等于這個表現在完全不可讀寫了。
△
▴
愉快的每一天