操作之前先上鎖,稱為悲觀鎖(Pessimistic Lock)。

InnoDB存儲引擎既支持行級鎖(row-level locking),也支持表級鎖,但默認情況下是採用行級鎖。表級鎖適合於以查詢為主,只有少量按索引更新數據的應用,如Web應用;而行級鎖適合於有大量按索引並發更新少量不同數據,同時又有並發查詢的應用,如一些在線事務處理(OLTP)系統。

InnoDB默認的讀取方式

編輯

一致性非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎通過多版本並發控制(MVVC)讀取當前數據庫中行數據的方式。如果讀取的行正在執行DELETE或UPDATE操作,這時讀取操作不會因此去等待行上鎖的釋放。相反地,InnoDB會去讀取行的一個快照。InnoDB是通過undo log來實現MVVC。undo log本身用來在事務中回滾數據,因此快照數據本身是沒有額外開銷。此外,讀取快照數據是不需要上鎖的,因為沒有事務需要對歷史的數據進行修改操作。

事務隔離級別為「提交讀」和「可重複讀」時,InnoDB使用一致性非鎖定讀。二者區別在於:「提交讀」事務隔離級別下,一致性非鎖定讀總是讀取被鎖定行的最新一份快照數據。而在「可重複讀」事務隔離級別下,則讀取本事務開始時的行數據版本。

MyISAM表鎖

編輯

MySQL的表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨占寫鎖(Table Write Lock)。 默認情況,select語句自動獲得讀鎖,update與delete語句自動獲得寫鎖。語法為:

Lock tables {Table_i_Name read|write [local]}+

在用LOCK TABLES給表顯式加表鎖時,必須同時取得所有涉及到表的鎖,並且MySQL不支持鎖升級。也就是說,在執行LOCK TABLES後,只能訪問顯式加鎖的這些表,不能訪問未加鎖的表(會報錯);同時,如果加的是讀鎖,那麼只能執行查詢操作,而不能執行更新操作(會報錯)。實際上在自動加鎖情況下也基本如此,總是一次獲得SQL語句所需要的全部鎖。這保證了不會出現死鎖(Deadlock Free)。

LOCK TABLES的「local」選項,其作用就是允許其他用戶在表尾並發插入記錄。

使用LOCK TABLES時,同一個表在SQL語句中用別名出現多少次,就要通過與SQL語句中相同的別名來鎖定多少次,否則也會報錯。例如:

 lock table actor as a read,actor as b read;
 unlock table

數據庫的狀態變量table_locks_waited(不能立即獲取表級鎖而需要等待的鎖請求次數)和table_locks_immediate(能夠立即獲得表級鎖的鎖請求次數)

默認的表鎖調度機制是寫優先。

InnoDB的行鎖

編輯
  • 行鎖
    • 共享鎖(S),又稱讀鎖:阻止其他事務加排他鎖。其他事務只能再加共享鎖。當前事務不能對行做任何修改。
    • 排他鎖(X),又稱寫鎖:允許獲取排他鎖的事務更新數據,阻止其他事務加共享讀鎖和排他寫鎖。由於普通select語句默認不會加任何鎖類型,所以仍然可以讀。
  • 表鎖:意向鎖(Intention Locks)
    • 意向共享鎖(IS):事務打算給數據行加共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
    • 意向排他鎖(IX):事務打算給數據行加排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。
共享表鎖(TR) 排他表鎖(TW) 意向共享鎖(IS) 意向排他鎖(IX)
共享表鎖(TR) 兼容 衝突 兼容 衝突
排他表鎖(TW) 衝突 衝突 衝突 衝突
意向共享鎖(IS) 兼容 衝突 兼容 兼容
意向排他鎖(IX) 衝突 衝突 兼容 兼容

對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);對於普通SELECT語句,InnoDB不會加任何鎖。事務可以通過以下select語句顯式給記錄集加共享鎖或排他鎖,主要用在需要數據依存關係時來確認某行記錄是否存在,並確保沒有人對這個記錄進行UPDATE或者DELETE操作:

  • 共享鎖(S),事務不會修改該行:SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
  • 排他鎖(X),事務可能會修改該行:SELECT * FROM table_name WHERE ... FOR UPDATE

InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點MySQL與Oracle不同,後者是通過在數據塊中對相應數據行加鎖來實現的。InnoDB這種行鎖實現特點意味着:只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖。訪問不同行的記錄,但如果是使用相同的索引鍵,會出現鎖衝突。MySQL會優化查詢的執行方式,未必會使用index的行鎖,需要用explain查看一條語句的實現方式。

下述語句查詢行鎖的衝突情況:

show status like 'innodb_row_lock%';

間隙鎖(Next-Key鎖)

編輯

當用範圍條件而不是相等條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖,並對鍵值在條件範圍內但並不存在的記錄(間隙GAP)也會被加鎖,這種鎖機制就是間隙鎖 (Next-Key鎖)。 這防止了幻影讀,即一個事務A第一次查詢獲得的結果後,別的事務插入了新事物,事務A第二次查詢時發現結果不同了。還有滿足事務恢復和複製的需求。

在實際應用開發中,尤其是並發插入的事務比較多的應用,要儘量優化業務邏輯,儘量使用相等條件來訪問、更新數據,避免使用範圍條件。