天天看點

MySQL技術内幕 InnoDB存儲引擎:一緻性非鎖定讀

一緻性的非鎖定行讀(consistent nonlocking read)是指InnoDB存儲引擎通過行多版本控制(multi versioning)的方式來讀取目前執行時間資料庫中行的資料。如果讀取的行正在執行DELETE、UPDATE操作,這是讀取操作不會是以而會等待行上鎖的釋放,相反,InnoDB會去讀取行的一個快照資料。

下圖直覺展示了一緻性的非鎖定行讀:

MySQL技術内幕 InnoDB存儲引擎:一緻性非鎖定讀

之是以稱其為非鎖定讀,因為不需要等待通路的行上X鎖的釋放。快照資料是指該行的之前版本的資料,該實作是通過undo段來完成。而undo段用來在此事務中復原資料,是以快照資料本身是沒有額外的開銷。此外,讀取快照資料是不需要上鎖的,因為沒有事務需要對曆史的資料進行修改操作。

可以看到,非鎖定讀機制極大地提髙了資料庫的并發性。在InnoDB存儲引擎的預設設定下,這是預設的讀取方式,即讀取不會占用和等待表上的鎖。但是在不同僚務隔離級别下,讀取的方式不同,并不是在每個事務隔離級别下都是采用非鎖定的一緻性讀。此外,即使都是使用非鎖定的一緻性讀,但是對于快照資料的定義也各不相同。

通過圖6-4可以知道,快照資料其實就是目前行資料之前的曆史版本,每行記錄可能有多個版本。就圖6-4所顯示的,一個行記錄可能有不止一個快照資料,一般稱這種技術為行多版本技術。由此帶來的并發控制,稱之為多版本并發控制(MultiVersionConcurrencyControl MVCC)

在事務隔離級别READ COMMITTED和REPEATABLE READ(InnoDB存儲引擎的預設事務隔離級别)下,InnoDB存儲引擎使用非鎖定的一緻性讀。然而,對于快照資料的定義卻不相同。在READ COMMITTED事務隔離級别下,對于快照資料,非一緻性讀總是讀取被鎖定行的最新一份快照資料。而在REPEATABLE READ事務隔離級别下,對于快照資料,非一緻性讀總是讀取事務開始時的行資料版本(關鍵在于事務之間的隔離性)。 來看下面的一個例子,首先在目前MySQL資料庫的連接配接會話A中執行如下SQL語句:

MySQL技術内幕 InnoDB存儲引擎:一緻性非鎖定讀

會話A中已認證顯式地執行指令BEGIN開啟了一個事務,并讀取了表parent中id為1.的資料,但是事務并沒有結束。與此同時,使用者再開啟另一個會話B,這樣可以模拟并發的情況,然後對會話B做如下的操作:

MySQL技術内幕 InnoDB存儲引擎:一緻性非鎖定讀

在會話B中将事務表parent中id為1的記錄修改為id=3,但是事務同樣沒有送出, 這樣id=1l的行其實加了一個X鎖。這時如果在會話A中再次讀取id為1的記錄,根據InnoDB存儲引擎的特性,即在READ COMMITTED和REPEATETABLE READ的事務隔離級别下會使用非鎖定的一緻性讀。回到之前的會話A,接着上次未送出的事務,執行SQL語句SELECT * FROM parent WHERE id=1的操作,這時不管使用READ COMMITTED還是REPEATABLE READ的事務隔離級别,顯示的資料應該都是:

MySQL技術内幕 InnoDB存儲引擎:一緻性非鎖定讀

由于目前id=l的資料被修改了1次,是以隻有一個行版本的記錄。接着,在會話B中送出上次的事務。

MySQL技術内幕 InnoDB存儲引擎:一緻性非鎖定讀

在會話B送出事務後,這時在會話A中再運作SELECT * FROM parent WHERE id=1的SQL語句,在READ COMMITTED和REPEATABLE事務隔離級别下得到結果 就不一樣了。對于READ COMMITTED的事務隔離級别,它總是讀取行的最新版本,如果行被鎖定了,則讀取該行版本的最新一個快照(fresh snapshot)。在上述例子中,因為會話B已經送出了事務,是以READ COMMITTED事務隔離級别下會得到如下結果:

MySQL技術内幕 InnoDB存儲引擎:一緻性非鎖定讀

而對于REPEATETABLE 的事務隔離級别,總是讀取事務開始時的行資料。是以對于REPEATETABLE READ事務隔離級别,其得到的結果如下:

MySQL技術内幕 InnoDB存儲引擎:一緻性非鎖定讀

下面将從時間的角度展現上述示範的示例過程,如表 6- 8 所示。需要特别注意的是,對于 READ COMMITTED 的事務隔離級别而言,從資料庫理論的角度來看,其違反了事務 ACID 中的 l 的特性,即隔離性。

MySQL技術内幕 InnoDB存儲引擎:一緻性非鎖定讀

本文整理自:《​​MySQL技術内幕 InnoDB存儲引擎​​》

個人微信公衆号: