MySQL數據庫鎖介紹

MySQL數據庫鎖介紹

1. 鎖的基本概念

當併發事務同時訪問一個資源時,有可能導致數據不一致,因此需要一種機制來將數據訪問順序化,以保證數據庫數據的一致性。
鎖就是其中的一種機制。
我們可以用商場的試衣間來做個比喻。商場裏得每個試衣間都可供多個消費者使用,因此可能出現多個消費者同時試衣服需要使用試衣間。爲了避免衝突,試衣間裝了鎖,某一個試衣服的人在試衣間裏把鎖鎖住了,其他顧客就不能再從外面打開了,只能等待裏面的顧客,試完衣服,從裏面把鎖打開,外面的人才能進去。

2. 鎖的基本類型

數據庫上的操作可以歸納爲兩種:讀和寫。
多個事務同時讀取一個對象的時候,是不會有衝突的。同時讀和寫,或者同時寫纔會產生衝突。因此爲了提高數據庫的併發性能,通常會定義兩種鎖:共享鎖和排它鎖。

2.1 共享鎖(Shared Lock,也叫S鎖)

共享鎖(S)表示對數據進行讀操作。因此多個事務可以同時爲一個對象加共享鎖。(如果試衣間的門還沒被鎖上,顧客都能夠同時進去參觀)
產生共享鎖的sql:select * from ad_plan lock in share mode;

2.2 排他鎖(Exclusive Lock,也叫X鎖)

排他鎖也叫寫鎖(X)。
排他鎖表示對數據進行寫操作。如果一個事務對對象加了排他鎖,其他事務就不能再給它加任何鎖了。(某個顧客把試衣間從裏面反鎖了,其他顧客想要使用這個試衣間,就只有等待鎖從裏面給打開了)
產生排他鎖的sql: select * from ad_plan for update;

對於鎖,通常會用一個矩陣來描述他們之間的衝突關係。
      S      X  
S    +      –  
X    –      –  
+ 代表兼容, - 代表不兼容
 

時間\事務

Tx1:

Tx2:

T1

set autocommit=0;

set autocommit=0;

T2

select * from ad_plan lock in share mode;

 

T3

 

update ad_plan set name='' ; blocking


執行sql: select * from information_schema.innodb_locks; 可以查看鎖。

3. 鎖的粒度

就是通常我們所說的鎖級別。MySQL有三種鎖的級別:頁級、表級、行級。
相對其他數據庫而言,MySQL的鎖機制比較簡單,其最 顯著的特點是不同的存儲引擎支持不同的鎖機制。
比如,MyISAM和MEMORY存儲引擎採用的是表級鎖(table-level locking);BDB存儲引擎採用的是頁面鎖(page-level locking),但也支持表級鎖;InnoDB存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但默認情況下是採用行級鎖。
MySQL這3種鎖的特性可大致歸納如下:
表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。
行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。
頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。
數據庫引擎通常必須獲取多粒度級別上的鎖才能完整地保護資源。

3.1 行鎖(Row Lock)

對一行記錄加鎖,隻影響一條記錄。
通常用在DML語句中,如INSERT, UPDATE, DELETE等。
InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點MySQL與Oracle不同,後者是通過在數據塊中對相應數據行加鎖來實現的。InnoDB這種行鎖實現特點意味着:只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!
用下面例子來說明一下:
CREATE TABLE test_index(id int , name VARCHAR(50),age int )engine=innodb ;
INSERT INTO test_index values(1,'張一',15);
INSERT INTO test_index values(3,'張三',16);
INSERT INTO test_index values(4,'張四',17);
INSERT INTO test_index values(5,'張五',19);
INSERT INTO test_index values(7,'劉琦',19);
不再啓用多事務描述了,直接解釋執行查詢語句
 explain select * from test_index where id = 1;
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | test_index | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+------------+------+---------------+------+---------+------+------+-------------+
type: all ,rows: 5 很明顯是會使用全表鎖。
增加索引,id加唯一索引,age加普通索引。
ALTER TABLE test_index
ADD UNIQUE uk_id(id),
ADD index idx_age(age);
mysql> explain select * from test_index where id = 1;
+----+-------------+------------+-------+---------------+-------+---------+-------+------+-------+
| id | select_type | table      | type  | possible_keys | key   | key_len | ref   | rows | Extra |
+----+-------------+------------+-------+---------------+-------+---------+-------+------+-------+
|  1 | SIMPLE      | test_index | const | uk_id         | uk_id | 5       | const |    1 | NULL  |
+----+-------------+------------+-------+---------------+-------+---------+-------+------+-------+

type: const ,key:uk_id,rows: 1 很明顯是會使用行鎖,鎖定一條記錄。

下面做個有趣的實驗:兩個事務,TX1加共享行鎖, 查詢age=17的記錄, TX2往數據庫裏插入一條age=18的記錄。
TX1:
mysql> set autocommit=0;
mysql> select * from test_index where age=17 lock in share mode;
+------+------+------+
| id   | name | age  |
+------+------+------+
|    4 | 張四 |   17 |
+------+------+------+

1 row in set (0.00 sec)


TX2:
mysql> set autocommit=0;
mysql> insert test_index values(8,'test',18);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

結果是TX2獲取鎖超時,看來TX1鎖定的並不止age=17的記錄,不存在的間隙age=18,也被加鎖了。

執行select * from information_schema.innodb_locks;可以看到加鎖的具體信息
+--------------+-------------+-----------+-----------+---------------------+------------+------------+-----------+----------+--------------------+
| lock_id      | lock_trx_id | lock_mode | lock_type | lock_table          | lock_index | lock_space | lock_page | lock_rec | lock_data          |
+--------------+-------------+-----------+-----------+---------------------+------------+------------+-----------+----------+--------------------+
| 45288:57:5:5 | 45288       | X,GAP     | RECORD    | `test`.`test_index` | idx_age    |         57 |         5 |        5 | 19, 0x000000000208 |
| 45289:57:5:5 | 45289       | S,GAP     | RECORD    | `test`.`test_index` | idx_age    |         57 |         5 |        5 | 19, 0x000000000208 |
+--------------+-------------+-----------+-----------+---------------------+------------+------------+-----------+----------+--------------------+


行鎖S、X鎖上做了一些精確的細分,在代碼中稱作Precise Mode。這些精確的模式,  使的鎖的粒度更細小。可以減少衝突。  
A.間隙鎖(Gap Lock),只鎖間隙。  
B.記錄鎖(Record Lock) 只鎖記錄。  
C.Next-Key Lock(代碼中稱爲Ordinary Lock),同時鎖住記錄和間隙。
D.插入意圖鎖(Insert Intention Lock),插入時使用的鎖。在代碼中,插入意圖鎖,實際上是GAP鎖上加了一個LOCK_INSERT_INTENTION的標記。

行鎖兼容矩陣

    G I R N
G + + + +
I + +
R + +
N + +
+ 代表兼容, -代表不兼容. 
G代表Gap鎖,I代表插入意圖鎖,R代表記錄鎖,N代表Next-Key鎖.  
S鎖和S鎖是完全兼容的,因此在判別兼容性時不需要對比精確模式。  
精確模式的檢測,用在S、X和X、X之間。
從這個矩陣可以看到幾個特點:  
A. INSERT操作之間不會有衝突。  
B. GAP,Next-Key會阻止Insert。  
C. GAP和Record,Next-Key不會衝突  
D. Record和Record、Next-Key之間相互衝突。  
E. 已有的Insert鎖不阻止任何準備加的鎖。

Gap lock:
間隙鎖只會出現在輔助索引(index)上,唯一索引(unique)和主鍵索引是沒有間隙鎖。
間隙鎖(無論是S還是X)只會阻塞insert操作。
間隙鎖的目的是爲了防止幻讀(但是需要應用自己加鎖,innodb默認不會加鎖防止幻讀)。

3.2 頁面鎖

3.3 表鎖(Table Lock)

對整個表加鎖,影響標準的所有記錄。通常用在DDL語句中,如DELETE TABLE,ALTER TABLE等。  
很明顯,表鎖影響整個表的數據,因此併發性不如行鎖好。
在MySQL 數據庫中,使用表級鎖定的主要是MyISAM,Memory等一些非事務性存儲引擎。

因爲表鎖覆蓋了行鎖的數據,所以表鎖和行鎖也會產生衝突(商場關門了,試衣間自然也沒法使用了)。如:
A. trx1 BEGIN
  B. trx1 給 T1 加X鎖,修改表結構。
  C. trx2 BEGIN
  D. trx2 給 T1 的一行記錄加S或X鎖(事務被阻塞,等待加鎖成功)
trx1要操作整個表,鎖住了整個表。那麼trx2就不能再對T1的單條記錄加X或S鎖,去讀取或修這條記錄。 

3.3.1 表鎖—意向鎖

爲了方便檢測表級鎖和行級鎖之間的衝突,就引入了意向鎖。
A. 意向鎖分爲意向讀鎖(IS)和意向寫鎖(IX)。  
B. 意向鎖是表級鎖,但是卻表示事務正在讀或寫某一行記錄,而不是整個表。     所以意向鎖之間不會產生衝突,真正的衝突在加行鎖時檢查。  
C. 在給一行記錄加鎖前,首先要給該表加意向鎖。也就是要同時加表意向鎖和行鎖。

採用了意向鎖後,上面的例子就變成了:
A. trx1 BEGIN  
B. trx1 給 T1 加X鎖,修改表結構。  
C. trx2 BEGIN  
D. trx2 給 T1 加IX鎖(事務被阻塞,等待加鎖成功)  
E. trx2 給 T1 的一行記錄加S或X鎖.

表鎖的兼容性矩陣
  IS IX S X
IS + + +
IX + +
S + +
X
+ 代表兼容, -代表不兼容
意向鎖之間不會衝突, 因爲意向鎖僅僅代表要對某行記錄進行操作。在加行鎖時,會判斷是否衝突。
發佈了79 篇原創文章 · 獲贊 193 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章