一致性非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎通過多版本控制(multi versionning)的方式來讀取當前執行時間數據庫中行的數據,如果讀取的行正在執行DELETE或UPDATE操作,這是讀取操作不會因此等待行上鎖的釋放。相反的,InnoDB會去讀取行的一個快照數據
上面展示了InnoDB存儲引擎一致性的非鎖定讀。之所以稱為非鎖定讀,因為不需要等待訪問的行上X鎖的釋放
快照數據是指該行之前版本的數據,該實現是通過undo段來完成。而undo用來事務中的回滾數據,因此快照數據本身沒有額外的開銷,此外,讀取快照數據不需要上鎖,因為沒有事務需要對歷史數據進行修改操作
可以看到,非鎖定讀機制極大地提高了數據庫的並發性,在InnoDB存儲引擎的默認設置下,這是默認的讀寫方式,即讀不會占用和等待表上的鎖。但是在不同的事務隔離級別下,讀取的方式不同,並不是每個事務隔離級別下都是采用非鎖定的一致性讀,此外,即使使用非鎖定的一致性讀,但是對於快照數據的定義也各不相同
快照其實是當前行數據之前的歷史版本,每行記錄可能有多個版本,如圖顯示,一個行記錄可能有不止一個快照數據,一般稱這種技術為多版本技術,因此帶來的並發控制。稱為多版本並發控制(Multi Version Concurrency Control,MVCC)
在事務隔離級別RC和RR下,InnoDB存儲引擎引擎使用非鎖定的一致性讀。然而,對於快照數據的定義卻不相同。在rc事務隔離級別下,對於快照數據,非一致性讀總是被鎖定行的最新一份快照數據.而在RR事務隔離級別下,對於快照數據,非一致性讀總是讀取事務開始時的行數據版本。
首先在MySQL的連接會話A中執行SQL語句
>BEGIN; >SELECT * FROM parent where id=1;
會話A中已經顯式的執行了BEGIN開啟了一個事務,並讀取了表parent中id為1的數據,但事務並沒有結束。於此同時,用戶開啟另一個會話B,這樣可以模擬並發的情況,然后對會話B做如下操作
>BEGIN; >UPDATE parent SET id=3 WHERE id=1;
會話B中將表parent中ID為1的改成3,但是同樣事務沒有提交,這樣id=1的行其實加了一個X鎖。這時如果在會話A中再次讀取id為1的記錄,根據InnoDB存儲引擎的特性,即在RC和RR事務隔離級別下會使用非鎖定的一致性讀。回到之前的會話A,接着上次未提交的事務,執行SQL語句SELECT * FROM parent WHERE id=1操作,這時不管是RC還是RR,顯示的數據都是1
由於當前id=1的數據被修改了1次,因此只有一個行版本的記錄,接着在會話B中提交上次的事務
>commint;
在會話B提交事務后,A中再次運行SELECT * FROM parent WHERE id=1,在RC和RR的事務隔離級別下得到的結果就不一樣了。在RC事務隔離級別,它總是讀取行的最新版本,如果行被鎖定,則讀取該行版本的最新一個快照(fresh snapshot)在上述例子中,因為B已經提交了事務,所以在RC隔離級別下的結果是
>select @@tx_isolation; 查看當前事務隔離級別 > SELECT * FROM parent WHERE id=1 結果為空
而對於RR隔離級別,總是讀取事務開始時的行數據,因此,對於RR事務隔離級別,得到的結果是
>select @@tx_isolation; 查看當前事務隔離級別 > SELECT * FROM parent WHERE id=1 結果為1