Mysql技術內幕之InnoDB鎖的深入講解
自7月份換工作以來,期間一直在學習MySQL的相關知識,聽了一些視頻課,但是一直好奇那些講師的知識是從哪里學習的。于是想著從書籍中找答案。畢竟一直
看視頻也不是辦法,不能形成自己的知識。于是想著看書汲取知識,看了幾本MySQL的相關書籍,包括《深入淺出Mysql》《高性能Mysql》《Mysql技術內幕》,發現那些講
師講的內容確實都在書上有出現過,于是確信看書才是正確的汲取知識方式。本片主要記錄了Mysql的鎖機制的學習。
1.什么是鎖鎖是計算機協調多個進程或線程并發訪問某一資源的機制。在數據庫中,除傳統的計算資源(如CPU、RAM、I/O等)的爭用以外,數據也是一種供許多用戶共享的資源。
如何保證數據并發訪問的一致性、有效性是所有數據庫必須解決的一個問題,鎖沖突也是影響數據庫并發訪問性能的一個重要因素。
相對其他數據庫而言,MySQL 的鎖機制比較簡單,其最顯著的特點是不同的存儲引擎支持不同的鎖機制。比如,MyISAM和MEMORY存儲引擎采用的是表級鎖(table-level
locking);BDB存儲引擎采用的是頁面鎖(page-levellocking),但也支持表級鎖;InnoDB存儲引擎既支持行級鎖(row-levellocking),也支持表級鎖,但默認情況下是采用行級鎖。
MySQL這3種鎖的特性可大致歸納如下。
表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,并發度最低。 行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,并發度也最高。 頁面鎖:開銷和加鎖時間界于表鎖和行鎖之間;會出現死鎖;鎖定粒度界于表鎖和行鎖之間,并發度一般。3種鎖的使用角度:
表級鎖更適合于以查詢為主,只有少量按索引條件更新數據的應用,如Web應用; 行級鎖則更適合于有大量按索引條件并發更新少量不同數據,同時又有并發查詢的應用,如一些在線事務處理(OLTP)系統。 BDB的頁面鎖已經被InnoDB取代,不做討論。2.InnoDB存儲引擎中的鎖2.1鎖的類型InnoDB存儲引擎實現了如下兩種標準的行級鎖:
共享鎖(S Lock),允許事務讀一行數據。 排他鎖(X Lock),允許事務刪除或更新一行數據。如果一個事務T1已經獲得了行r的共享鎖,那么另外的事務T2可以立即獲得行r的共享鎖,因為讀取沒有改變行r的數據,稱這種情況
為鎖兼容(Lock Compatible)。但若有其他的事務T3想獲得行r的排他鎖,則其必須等待事務T1、T2釋放行r的共享鎖——這種情況稱為鎖不兼容。
X S X 不兼容 不兼容 S 不兼容 兼容
此外,InnoDB存儲引擎支持多粒度鎖定,這種鎖定允許事務在行級上鎖和表鎖上的鎖同時存在。為了支持在不同粒度上進行加鎖操作,InnoDB存
儲引擎支持一種額外的鎖方式,稱之為意向鎖。意向鎖是將鎖定的對象分為多個層次,意向鎖意味著事務希望在更細粒度上進行加鎖。 InnoDB存
儲引擎支持意向鎖設計比較簡練,其意向鎖即為表級別的鎖。設計目的主要是為了在一個事務中揭示下一行將被請求的鎖類型。其支持兩種意向鎖:
意向共享鎖(IS Lock),事務想要獲得一張表中某幾行的共享鎖 意向排他鎖(IX Lock),事務想要獲得一張表中某幾行的排他鎖2.2 一致性非鎖定讀一致性的非鎖定讀(consistant nonlocking read)是指InnoDB存儲引擎通過多版本控制(multi versioning)的方法來讀取當前執行時間數據庫中行的
數據。如果讀取的行正在執行Delete或Update操作,這時讀取操作不會因此去等待行上鎖的釋放。相反地,InnoDB存儲引擎會去讀取行的一個快照
版本。如下如所示。
上圖直觀地展現了InnoDB存儲引擎一致性的非鎖定讀。之所以稱為非鎖定讀,因為不需要等待訪問的行上X鎖的釋放。快照數據是指該行的之前版本
的數據,該實現是通過undo段來完成。而undo用來在事務中回滾數據,因此快照數據本身是沒有額外的開銷。此外,讀取快照數據是不需要上鎖的,
因為沒有事務需要對歷史的數據進行修改操作。
通過上圖可以知道,快照數據其實就是當前行數據之前的歷史版本,每行記錄可能有多個版本,一般稱這種技術為行多版本技術。由此帶來的并發控制,
稱之為多版本并發控制(Multi Version Concurrency Control, MVCC)。
在事務隔離級別READ COMMITTED和REPEATABLE READ下,InnoDB存儲引擎使用非鎖定的一致性讀。然而,對于快照數據的定義卻不相同。在READ
COMMITTED事務隔離級別下,對于快照數據,非一致性讀總是讀取被鎖定行的最新一份快照數據。而在REPEATABLE READ事務隔離級別下,對于快照
數據,非一致性讀總是讀取事務開始時的行數據版本。如下表所示示例:
時間 會話A 會話B 1 begin 2 select * from t_user where id = 1; 3 begin 4 update t_user set id = 10 where id = 1; 5 select * from t_user where id = 1; 6 commit; 7 select * from t_user where id = 1; 8 commit;
假設原本id = 1的記錄是存在的,大家可以按上表時間順序執行對應的會話,比較及驗證2者的不同。
2.3 一致性鎖定讀在默認配置下,在事務的隔離級別為REPEATABLE READ模式下,InnoDB存儲引擎的select操作使用一致性非鎖定讀。但是在某些情況下,用戶需要顯示地
對數據庫讀取操作進行加鎖以保證數據邏輯的一致性。而這要求數據庫支持加鎖語句,即使時對于select的只讀操作。InnoDB存儲引擎對于select語句支持兩
種一致性的鎖定讀(locking read)操作:
select ··· for update select ··· lock in share modeselect ··· for update對讀取的行記錄加一個X鎖,其他事務不能對已鎖定的行加上任何鎖。select ··· lock in share mode對讀取的行記錄加一個S鎖,其他事務可
以向被鎖定的行加S鎖,但是如果加X鎖,則會被阻塞。
對于一致性非鎖定讀,即使讀取的行已被執行了select ··· for update,也是可以進行讀取的。此外,select ··· for update或者select ··· lock in share mode必須在
一個事務中,當事務提交了,鎖也就釋放了。因此在使用上述兩種select鎖定語句時,務必加上begin,start transaction或者set autocommit=0。
3 鎖的算法3.1行鎖的3中算法InnoDB存儲引擎有3種行鎖的算法,其分別是:
Record Lock:單個行記錄上的鎖 Gap Lock:間隙鎖,鎖定一個范圍,但不包含記錄本身 Next-Key Lock:Gap Lock + Record Lock,鎖定一個范圍,并且鎖定記錄本身Record Lock總是會去鎖住主鍵索引記錄,如果InnoDB存儲引擎表在建立的時候沒有設置任何一個主鍵或唯一非空索引,那么這時InnoDB存儲引擎會使用隱式的
主鍵來進行鎖定。
Next-Key Lock是結合了Gap Lock+Record Lock的一種鎖定算法,在Next-Key Lock算法下,InnoDB對于行的查詢都是采用這種鎖定算法。假如一個索引有10,11
,13和20這4個值,那么該索引可能被Next-Key Locking的區間為:
(-無窮,10] ,(10,11], (11,13], (13,20], (20,+無窮)
采用Next-Key Lock的鎖定技術稱為Next-Key Locking。其設計的目的是為了解決幻讀問題。而利用這種鎖定技術,鎖定的不是單個值,而是一個范圍。 然而,
當查詢的索引含有唯一屬性時,InnoDB存儲引擎會對Next-Key Lock進行優化將其降級為Record Lock,即僅鎖住索引本身,而不是范圍。下面演示一個例子。
mysql> create table t (a int primary key);Query OK, 0 rows affected (0.01 sec)mysql> insert into t select 1;Query OK, 1 row affected (0.00 sec)Records: 1 Duplicates: 0 Warnings: 0mysql> insert into t select 2;Query OK, 1 row affected (0.00 sec)Records: 1 Duplicates: 0 Warnings: 0mysql> insert into t select 5;Query OK, 1 row affected (0.01 sec)Records: 1 Duplicates: 0 Warnings: 0
接著按下表時間順序執行操作。
時間 會話A 會話B 1 begin; 2 select * from t where a = 5 for update; 3 begin; 4 insert into t select 4; 5 commit; #成功,不需要等待 6 commit;
表t共有1,2,5三個值。在上面的例子中,在會話A中首先對a=5進行X鎖定。而由于a是主鍵且唯一,因此鎖定的僅是5這個值,而不是(2,5)這個范圍,這樣在會話
B中插入值4而不會阻塞,可以立即插入并返回。即鎖定由Next-Key Lock算法降級為了Record Lock,從而提高應用的并發性。
如上,Next-Key Lock降級為Record Lock僅在查詢的列是唯一索引的情況下。若是輔助索引,則情況會完全不同。同樣,首先創建測試表z進行測試:
mysql> create table z (a int ,b int ,primary key(a), key(b));mysql> insert into z select 1,1;mysql> insert into z select 3,1;mysql> insert into z select 5,3;mysql> insert into z select 7,6;mysql> insert into z select 10,8;
表z的列b是輔助索引,若在會話A中執行下面的SQL語句:
mysql> select * from z where b = 3 for update;
很明顯,這時SQL語句通過索引列b進行查詢,因此其使用傳統的Next-Key Locking技術加鎖,并且由于有兩個索引,其需要分別進行鎖定。對于聚集索引,其僅對列
a等于5的索引加上Record Lock。而對于輔助索引,其加上的是Next-Key Lock,鎖定的范圍是(1,3),特別需要注意的是,InnoDB存儲引擎還會對輔助索引下一個
鍵值加上gap lock,即還有一個輔助索引范圍為(3,6)的鎖。因此,若在新會話B中運行下面的SQL語句,都會被阻塞:
mysql> select * from z where a = 5 lock in share mode;mysql> insert into z select 4,2;mysql> insert into z select 6,5;
第一個SQL語句不能執行,因為在會話A中執行的SQL語句已經對聚集索引中列a=5的值加上X鎖,因此執行會被阻塞。第二個SQL語句,主鍵插入4,沒有問題,但是插入
的輔助索引值2在鎖定的范圍(1,3)中,因此執行同樣會被阻塞。第三個SQL語句,插入的主鍵6沒有被鎖定,5也不在范圍(1,3)之間。但插入的值5在另一個鎖定的
范圍(3,6)中,故同樣需要等待。而下面的SQL語句,不會被阻塞,可以立即執行:
mysql> insert into z select 8,6;mysql> insert into z select 2,0;mysql> insert into z select 6,7;
從上面的例子可以看到,Gap Lock的作用是為了阻止多個事務將記錄插入到同一個范圍內,而這會導致幻讀問題的產生。假如在上面的例子中,會話A中用戶已經鎖定了
b=3的記錄。若此時沒有Gap Lock鎖定(3,6),那么用戶可以插入索引b列為3的記錄,這會導致會話A中的用戶再次執行同樣查詢時會返回不同的記錄,即幻讀。
這里主要探究的是InnoDB存儲引擎鎖表的機制,至少自己明白了Mysql的行鎖機制,不知道讀者是否有疑問,歡迎留言。下次會記錄關于Mysql事務特性及其內部的實現機制,
包括mysql的內部架構,InnoDB buffer Pool,redo log, undo log等具體的詳解,目前只是對知識過了一遍,但還未總結。
總結到此這篇關于Mysql技術內幕之InnoDB鎖的文章就介紹到這了,更多相關Mysql InnoDB鎖內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!
相關文章:
1. mysql數據存放的位置在哪2. 在SQL Server中用XQuery分解XML數據3. Mysql 用戶權限管理實現4. Microsoft Office Access修改代碼字體大小的方法5. MySQL存儲過程例子(包含事務、參數、嵌套調用、游標循環等)6. Mysql入門系列:安排預防性的維護MYSQL數據庫服務器7. MySQL 字符串函數:字符串截取8. 恢復從 Access 2000、 Access 2002 或 Access 2003 中數據庫刪除表的方法9. MySQL中InnoDB和MyISAM類型的差別10. MySQL 8.0 之索引跳躍掃描(Index Skip Scan)
