6 - 鎖
lock 和 latch
latch:內存中的輕量級互斥量或讀寫鎖,其目的是為了保證併發線程操作臨界資源的正確性。無死鎖檢測和處理機制。
lock:鎖定數據庫中的對象,比如表,頁,行等,其目的是為了保證併發事務操作表對象的正確性。可通過 等待有向圖 和 超時機制進行死鎖檢測和處理。
以下討論的內容都是和lock相關的。
Innodb中鎖的類型
行級鎖:
- 共享鎖(讀鎖):可以和其他共享鎖並行
- 排它鎖(寫鎖):不能和任何其他鎖並行
表級鎖:
- 共享鎖
- 排他鎖
- 意向共享鎖
- 意向排他鎖
意向鎖之間不互斥,意向鎖和任何行級鎖之間不互斥
意向鎖只和表級鎖互斥,其存在目的是為了快速判斷該表是否有行級讀鎖或寫鎖,便於快速阻塞表鎖獲取。
一致性非鎖定讀 by MVCC
MVCC:每個被更新行記錄在事務提交之前,會在undo中存儲該行之前版本的數據快照。每行記錄可能會有多條undo保存多個版本的快照,因此稱為多版本併發控制。
不同隔離級別對使用的快照定義不同
Read Committed:總是讀取最新一條快照的數據
Read Repeatable:總是讀取事務開始時的快照數據
一致性鎖定讀:
對讀取的記錄加X鎖:select ... for update
對讀取的記錄加S鎖:select ... lock in share mode
行級鎖的算法
- Record Lock:行鎖,鎖定單條記錄
- Gap Lock:間隙鎖,鎖定一個範圍,但不包括記錄本身
- Next-Key Lock:行鎖+間隙鎖,鎖定一個範圍,且包括記錄本身
Next-Key Lock的存在目的是為了解決Read Repeatable隔離級別下的幻讀問題。當按索引範圍查詢數據時,其鎖定範圍上界到上一個索引以及下界到下一個索引,使這個範圍內無法添加或刪除行記錄,保證多次讀取的行數據一致。
鎖問題
- 髒讀:
讀到其他事務還未提交的數據。Fix: I >= Read Committed - 不可重複讀/幻讀
事務內多次讀取同一數據集合,由於中途有其他事務提交,導致兩次讀取讀到的數據可能是不一樣的。 有兩種不一樣的可能:數據內容不一致 OR 數據行數不一致。I >= Read Repeatable - 丟失更新
業務邏輯導致的數據更新丟失,可以考慮將數據獲取-更新邏輯串行化,或採用樂觀鎖機制來保證數據一致性。
死鎖檢測
對事務-鎖進行建模,將其依賴關係構建成一個有向依賴圖,當有向圖中出現環的時候,即代表出現了死鎖。
7 - 事務
事務的基礎 ACID
- A 原子性:一個事務要麼全部執行,要麼全部不執行
- C 一致性:事務執行總是從一個滿足一致性的狀態到另一個一致性狀態
- I 隔離性:事務和事務之間在執行中互相隔離
- D 持久性:事務執行一旦成功,其結果就是永久性的
事務的實現
- redo log: 持久性
- undo log: 原子性
- 鎖: 隔離性,一致性
undo log
不同於redo log的物理修改,undo log存儲的是回滾的邏輯修改。
undo log分為兩種類型
- insert undolog:對應insert操作,新增的數據只對事務本身可見,因此提交後可直接刪除。
- update undolog:對應update,delete操作。該類undolog需要提供MVCC機制,因此提交後被放入待purge鏈表,在不被任何事務引用後被purge刪除。
undo 和 MVCC
每行數據裏記錄了最後一條修改該行的trx_id,同時記錄了最後一次修改的roll_back_ptr。和行數據類似,update undolog裏也記錄了trx_id和roll_back_ptr。事務執行時,可以通過比對trx_id(trx_id單調遞增)沿着roll_back_ptr一直找到事務開始前的最新一條記錄,即RR級別下的行快照。
分佈式事務 & 二階段提交
分佈式事務使用兩段式提交,第一階段,所有參與事務的節點都進行準備,告知事務管理器它們準備好了。第二階段,由事務管理器告知節點執行rollback還是commit。
binlog和redo log間的二階段提交
binlog和redo log的寫入需要是原子的,以防止在複製從節點時出現數據異常問題。mysql通過在binlog和innodb間使用二階段提交來解決該問題。具體執行順序如下:
- innodb prepare 事務,將事務的xid寫入
- mysql 寫入 binlog
- 事務提交,innodb 寫入 redolog
宕機後,檢查xid事務是否提交,如未提交則主動進行一次提交,如果binlog沒寫入,那麼額外寫入一次binlog。因此在2,3任一環節宕機,都可以保證數據一致性。