1. 定義
????????MVCC是多版本并發控制(Multi - Version Concurrency Control)的縮寫。它是InnoDB存儲引擎實現高并發控制的一種機制。在數據庫系統中,多個事務可能會同時對數據進行讀寫操作,而MVCC通過為數據行保存多個版本來解決并發事務之間的沖突問題,使得數據庫在保證數據一致性的同時,能夠支持更多的并發操作。MVCC的核心思想是:為每一行數據維護多個版本,使得讀寫操作可以并發執行而不互相阻塞。
2. 事務和事務ID
????????在InnoDB中,事務是MVCC的核心。每個事務在開始時都會被分配一個唯一的事務ID(Transaction ID,簡稱TXID)。事務ID是單調遞增的,用于標識事務的先后順序。
-
事務ID的作用:
-
事務ID用于標記數據版本的創建者和可見性。
-
事務ID用于判斷事務之間的先后順序,從而實現并發控制。
-
3. 數據版本的存儲結構?
????????InnoDB使用行版本控制來實現MVCC。每行數據在物理存儲上可能有多個版本,這些版本通過隱藏的列和指針進行管理。
-
隱藏列:
-
DB_TRX_ID(事務ID):記錄最后一次對該行進行修改的事務的ID。這個ID是遞增的,每次事務開始時都會分配一個唯一的事務ID。當一個事務對數據行進行修改時,InnoDB會將這個事務的ID記錄在DB_TRX_ID列中。
-
DB_ROLL_PTR(回滾指針):回滾指針指向該行數據的舊版本。舊版本數據存儲在InnoDB的回滾段(Undo Log)中。Undo Log是InnoDB用來存儲數據行舊版本的地方,它記錄了數據行在各個事務修改前的狀態。
-
DB_ROW_ID:行ID,用于唯一標識一行數據。
-
-
版本鏈:
-
每行數據的多個版本通過DB_ROLL_PTR鏈接在一起,形成一個版本鏈。
-
最新的版本存儲在表空間中,舊版本存儲在回滾段(Undo Log)中。
-
-
ReadView:
???決定當前事務能看到哪些版本的數據,包含:?-
m_ids
:當前活躍(未提交)事務ID列表。 -
min_trx_id
:m_ids中的最小值。 -
max_trx_id
:下一個將分配的事務ID。 -
creator_trx_id
:創建該ReadView的事務ID。
-
4. MVCC的可見性規則
????????InnoDB通過事務ID和版本鏈來判斷數據版本的可見性。具體規則如下:
4.1 讀操作的可見性規則
-
一致性讀(Consistent Read):
-
默認情況下,InnoDB使用一致性讀,即讀取數據時會看到事務開始時的數據庫快照。
-
事務只能看到在它開始之前已經提交的版本,或者它自己插入的版本。
-
事務不能看到在它開始之后其他事務插入或更新的版本。
-
-
活躍事務是指在當前事務開始時仍然在運行的事務。換句話說,這些事務可能尚未提交或回滾。
示例:假設當前數據庫中有以下事務:-
事務ID為
102
的事務也正在運行。 -
事務ID為
101
的事務正在運行(尚未提交或回滾)。 -
事務ID為
100
的事務已經提交。 -
現在,一個新的事務(事務ID為
103
)開始執行。對于事務103
來說: -
活躍事務:事務ID為
101
和102
的事務,因為它們在事務103
開始時仍在運行。 -
非活躍事務:事務ID為
100
的事務,因為它在事務103
開始之前已經提交。
-
-
快照讀的判斷邏輯:
-
數據版本:記錄了每條記錄在不同時間點的狀態。
-
活躍事務:記錄了在當前事務開始時仍在運行的事務。
-
如果一個數據版本的
DB_TRX_ID
小于當前事務的最小活躍事務ID:-
這個版本是由一個已經提交的事務生成的,對當前事務可見。
-
-
如果一個數據版本的
DB_TRX_ID
大于當前事務的最大活躍事務ID:-
這個版本是由一個在當前事務開始之后才開始的事務生成的,對當前事務不可見。
-
-
如果一個數據版本的
DB_TRX_ID
在當前事務的活躍事務列表中:-
這個版本是由一個仍在運行的事務生成的,對當前事務不可見。需要回溯到舊版本,繼續判斷,直到找到一個符合上述條件的版本。
-
-
4.2 寫操作的可見性規則
-
當前讀(Current Read):
-
寫操作(如INSERT、UPDATE、DELETE)會獲取行鎖,并直接操作最新的數據版本。
-
寫操作會生成新的數據版本,并更新
DB_TRX_ID
為當前事務ID。 -
如果是更新操作,舊版本會被移動到回滾段中,并通過
DB_ROLL_PTR
鏈接。
-
5.?事務的隔離級別與MVCC
????????InnoDB支持多種事務隔離級別,不同隔離級別對MVCC的實現方式和可見性規則有所不同。
4.1 讀已提交(Read Committed)
-
特點:
-
事務只能看到在它開始之前已經提交的版本。
-
每次讀取數據時,InnoDB都會重新構建一個快照。
-
不允許讀取未提交的版本,避免了“臟讀”問題。
-
-
MVCC實現:
-
對于每個SELECT操作,InnoDB會根據當前事務的讀視圖,找到最新的已提交版本。
-
如果數據被其他事務鎖定,當前事務會等待鎖釋放。
-
5.2 可重復讀(Repeatable Read)
-
特點:
-
事務在執行期間看到的數據庫快照是一致的,即使其他事務在并發修改數據。
-
避免了“不可重復讀”問題。
-
-
MVCC實現:
-
事務開始時,InnoDB會創建一個快照視圖,后續的讀操作都基于這個快照。
-
事務只能看到在它開始之前已經提交的版本,或者它自己插入的版本。
-
如果其他事務在并發修改數據,當前事務不會看到這些修改,除非它自己也進行了修改。
-
5.3 串行化(Serializable)
-
特點:
-
最嚴格的隔離級別,事務之間完全隔離。
-
避免了所有并發問題,但性能開銷最大。
-
-
MVCC實現:
-
除了使用MVCC的版本控制外,還會通過加鎖機制(如表鎖或行鎖)來確保事務之間的串行執行。
-
讀操作也會加鎖,防止其他事務修改數據。
-
6. 回滾段(Undo Log)的作用
????????回滾段是InnoDB實現MVCC的關鍵組件之一。它用于存儲數據的舊版本,以便支持一致性讀和事務回滾。
-
存儲舊版本:
-
當事務更新數據時,舊版本會被移動到回滾段中,并通過
DB_ROLL_PTR
鏈接。 -
回滾段中的舊版本數據用于支持一致性讀,事務可以根據需要回溯到舊版本。
-
-
事務回滾:
-
如果事務需要回滾,InnoDB會通過回滾段中的舊版本數據恢復到事務開始之前的狀態。
-
-
垃圾回收:
-
當沒有事務再需要回滾段中的舊版本數據時,InnoDB會進行垃圾回收,釋放回滾段的空間。
-
7.?總結
? ? ? ? InnoDB的MVCC機制是一種高效的并發控制機制,它通過為數據行保存多個版本,實現了讀操作和寫操作之間的高并發性,減少了鎖競爭,并保證了事務的一致性讀。然而,MVCC機制也存在一些局限性,如版本鏈過長、垃圾回收問題和快照隔離級別下的幻讀問題。盡管如此,InnoDB的MVCC機制仍然是現代數據庫系統中一種重要的并發控制機制,廣泛應用于各種高并發的場景中。