1.innodb 的鎖分類
- 列粒度鎖:S 共享鎖 X 排他鎖
- 意向鎖:IS 意向共享鎖 IX 意向排他鎖
1.1.意向鎖:
- innodb 的鎖支援多粒度鎖定。為了實現多粒度鎖,innodb 通過意向鎖(IS共享意向鎖、IX共享排他鎖)的方式實現。
- 在細粒度上加鎖,則需要先在粗粒度上加意向鎖。比如,如果需要在記錄行加鎖,則先要在表上加意向鎖,最後在行上加上 X/S 鎖 。任何一個加鎖操作導致的等待都需要等待粗粒度鎖釋放。
- 由於 innodb 支援的是行級鎖,所以意向鎖並不會阻塞除掃描全表外的任何請求。
1.2.鎖的相容列表
專案 | IS | IX | S | X |
---|---|---|---|---|
IS | 相容 | 相容 | 相容 | 不相容 |
IX | 相容 | 相容 | 不相容 | 不相容 |
S | 相容 | 不相容 | 相容 | 不相容 |
X | 不相容 | 不相容 | 不相容 | 不相容 |
2.顯式加鎖方式:
- 共享鎖:SELECT * LOCK IN SHARE MODE
- 排他鎖:SELECT * FOR UPDATE
3.一致性非鎖定讀:
是指 innodb 通過多版本控制的方式來讀取當前執行時間資料庫中的資料。如果,讀取的時候行正好在執行 DELETE 或 UPDATE 的排他操作,那讀取操作不會等待,會直接去讀取一個快照版本的行資料。
3.1 MVCC(多版本併發控制):
一行資料可能不止一個快照,實際上是通過 undo 段來完成的
3.2 事務隔離級別對一致性非鎖定讀的影響:
- READ COMMITED:此時,讀取的內容是快照中最新的資料版本。
- REPEATABLE READ:此時,讀取的內容是讀取事務開始時候的資料版本。
- 詳解:區別就是,當讀取事務開始時候的資料版本,即是獲取的讀取操作事務開始時候的資料版本,在這個讀取事務期間寫入的其他內容都不會影響讀取版本。READ COMMITED 則會讀取被修改後的版本。所以,實際上READ COMMITED 這個事務級別在這種情況下會失去事務的隔離級別。
3.2 一致性鎖定讀:
則是通過在事務中顯式的用 FOR UPDATE 和 LOCK IN SHARE MODE 加鎖。當事務提交後鎖才會釋放。
4.自增長與鎖:
- 自增長的鎖是特殊處理的,並不是在事務完成後才釋放。是在自增長值被 SQL 語句插入成功就會釋放。
4.1影響:
- 有自增長值的表因為鎖的存在併發插入效能比較差
- 大資料量的插入會影響插入效能,因為另一個事務中的插入會被阻塞。
5.鎖的演算法:
- Record Lock 單行記錄的鎖
- Gap Lock 鎖定一個範圍,但是不包含記錄本身
- Next-Key Lock :Gap Lock + Record Lock 鎖定一個範圍,並且包含記錄自身。
5.1 Next-Key Lock 細節:
- 在預設情況下,如果索引非唯一,類似於索引有鍵1,3,5,7。這樣Next-key Lock則會包含這幾個範圍的鎖(-∞,1],(1,3],(3,5],(5,7],(7,+∞)。
- 如果表在建立的時候沒有設定索引,innodb 會預設使用主鍵來進行鎖定。
5.2鎖的降級:
當索引含有唯一屬性時,Next-key Lock會自動降級為Record Lock 用來減少鎖定的範圍,加大併發的處理速度。但是此種情況只存在於查詢【所有的唯一索引列】。如果,唯一索引由多個列組成,而查詢是查詢多個唯一索引列中的其中一個,那麼這種查詢由於聯合索引的特性,查詢是一個範圍查詢,而不是點查詢,所以不會降級處理。
5.3如何規避幻讀:
當某個表有2個索引,一個聚集唯一索引,一個輔助索引。有(1,1)(2,3),(3,5),(4,7)這4個資料行。執行SELECT * FROM T WHERE b=5 FOR UPDATE。此時聚集唯一索引因為鎖降級的優化,會對a=3加上Record Lock,但是為了避免幻讀,輔助索引會有2種鎖,第一種是b=5的Record Lock,還有(3,5)(5,7) 2個範圍的Grap lock。由於索引 B+tree 的屬性,聯合索引 a列又在b列之前,所以索引的節點值是
[舉例,索引不一定真的是這種結構]
[] (3,5) []
/ \
[(1,1)(2,3)] [(3,5)(4,7)]
複製程式碼
如果沒有鎖定b 列的(5,7)範圍我再insert (4,5) 是可以插入進去的,因為a=3和 b=5都被 record lock 鎖定了,但是按聯合索引的特性是按前序列進行排序插入的,此時並不會觸發到b=5的 record lock。這樣就會導致幻讀,連續執行SELECT * FROM T WHERE b=5 FOR UPDATE這個語句上一個事務的結果與下一次執行的這個語句得到的結果不會一樣。
- oracle 資料庫必須要在事務級別是 SERIALIZABLE 級別才能避免髒讀,但是 innodb 通過這種範圍鎖的騷操作,直接在REPEATABLE READ以上的事務級別,通過使用Next-key lock的鎖級別,避免了幻讀
6.鎖問題:
mysql innodb 的預設事務級別是 REPEATABLE READ
- 髒讀:一個事務讀取到了另外一個事務沒有提交的資料(也稱作髒資料)。破壞了資料庫的隔離性
- 不可重複讀(幻讀): A事務裡面先讀取了部分資料,然後B事務對讀取的這部分資料做了操作,然後A 事務又來讀取這部分資料,則會出現 A 事務的兩次對同一部分資料獲取的結果不同。違反了資料庫的事務一致性要求。
- 丟失更新:A 事務對 a 列進行了修改為 2 未提交時,B 事務更新 a 列為 3,未提交,此時 A B 兩個事務提交。必將導致結果不是預期的。由於排他鎖的介入,實際上資料層面不會發生這種操作,因為 A 事務在寫的時候記錄是被鎖住的,其他事務是會被阻塞的。所以,這種情況會發生在應用資料庫的更上層。如果需要在資料庫層面解決這種問題,在讀取記錄的時候也需要加上 X 鎖,在操作完成後再釋放,此時可以避免不可重複讀。
7.阻塞和死鎖
略過,可以在其他層面理解。目前還沒有深入瞭解的打算
8.鎖升級:
由於 innodb 的鎖根據頁粒度進行加鎖,並採取點陣圖的方式,鎖資源開銷較小,不存在鎖升級的操作。