您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關MySQL中InnoDB的一致性非鎖定讀是怎么樣的,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
?一致性非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎通過多版本控制(MVVC)讀取當前數據庫中行數據的方式。如果讀取的行正在執行DELETE或UPDATE操作,這時讀取操作不會因此去等待行上鎖的釋放。相反地,InnoDB會去讀取行的一個快照。
?上圖直觀地展現了InnoDB一致性非鎖定讀的機制。之所以稱其為非鎖定讀,是因為不需要等待行上排他鎖的釋放。快照數據是指該行的之前版本的數據,每行記錄可能有多個版本,一般稱這種技術為行多版本技術。
由此帶來的并發控制,稱之為多版本并發控制(Multi Version Concurrency Control, MVVC)。InnoDB是通過undo log來實現MVVC。undo log本身用來在事務中回滾數據,因此快照數據本身是沒有額外開銷。此外,讀取快照數據是不需要上鎖的,因為沒有事務需要對歷史的數據進行修改操作。
?一致性非鎖定讀是InnoDB默認的讀取方式,即讀取不會占用和等待行上的鎖。但是并不是在每個事務隔離級別下都是采用此種方式。此外,即使都是使用一致性非鎖定讀,但是對于快照數據的定義也各不相同。
?在事務隔離級別READ COMMITTED和REPEATABLE READ下,InnoDB使用一致性非鎖定讀。然而,對于快照數據的定義卻不同。在READ COMMITTED事務隔離級別下,一致性非鎖定讀總是讀取被鎖定行的最新一份快照數據。而在REPEATABLE READ事務隔離級別下,則讀取事務開始時的行數據版本。
?我們下面舉個例子來詳細說明一下上述的情況。
# session A
mysql> BEGIN;
mysql> SELECT * FROM test WHERE id = 1;
?我們首先在會話A中顯示地開啟一個事務,然后讀取test表中的id為1的數據,但是事務并沒有結束。于此同時,用戶在開啟另一個會話B,這樣可以模擬并發的操作,然后對會話B做出如下的操作:
# session B
mysql> BEGIN;
mysql> UPDATE test SET id = 3 WHERE id = 1;
?在會話B的事務中,將test表中id為1的記錄修改為id=3,但是事務同樣也沒有提交,這樣id=1的行其實加了一個排他鎖。由于InnoDB在READ COMMITTED和REPEATABLE READ事務隔離級別下使用一致性非鎖定讀,這時如果會話A再次讀取id為1的記錄,仍然能夠讀取到相同的數據。此時,READ COMMITTED和REPEATABLE READ事務隔離級別沒有任何區別。
?如上圖所示,當會話B提交事務后,會話A再次運行SELECT * FROM test WHERE id = 1
的SQL語句時,兩個事務隔離級別下得到的結果就不一樣了。
?對于READ COMMITTED的事務隔離級別,它總是讀取行的最新版本,如果行被鎖定了,則讀取該行版本的最新一個快照。因為會話B的事務已經提交,所以在該隔離級別下上述SQL語句的結果集是空的。
?對于REPEATABLE READ的事務隔離級別,總是讀取事務開始時的行數據,因此,在該隔離級別下,上述SQL語句仍然會獲得相同的數據。
?我們首先來看一下wiki上對MVVC的定義:
Multiversion concurrency control (MCC or MVCC), is a concurrency control
method commonly used by database management systems to provide
concurrent access to the database and in programming languages to
implement transactional memory.
?由定義可知,MVVC是用于數據庫提供并發訪問控制的并發控制技術。
數據庫的并發控制機制有很多,最為常見的就是鎖機制。鎖機制一般會給競爭資源加鎖,阻塞讀或者寫操作來解決事務之間的競爭條件,最終保證事務的可串行化。
而MVVC則引入了另外一種并發控制,它讓讀寫操作互不阻塞,每一個寫操作都會創建一個新版本的數據,讀操作會從有限多個版本的數據中挑選一個最合適的結果直接返回,由此解決了事務的競爭條件。
?考慮一個現實場景。管理者要查詢所有用戶的存款總額,假設除了用戶A和用戶B之外,其他用戶的存款總額都為0,A、B用戶各有存款1000,所以所有用戶的存款總額為2000。但是在查詢過程中,用戶A會向用戶B進行轉賬操作。轉賬操作和查詢總額操作的時序圖如下圖所示。
?如果沒有任何的并發控制機制,查詢總額事務先讀取了用戶A的賬戶存款,然后轉賬事務改變了用戶A和用戶B的賬戶存款,最后查詢總額事務繼續讀取了轉賬后的用戶B的賬號存款,導致最終統計的存款總額多了100元,發生錯誤。
?使用鎖機制可以解決上述的問題。查詢總額事務會對讀取的行加鎖,等到操作結束后再釋放所有行上的鎖。因為用戶A的存款被鎖,導致轉賬操作被阻塞,直到查詢總額事務提交并將所有鎖都釋放。
?MVCC使得數據庫讀不會對數據加鎖,普通的SELECT請求不會加鎖,提高了數據庫的并發處理能力。借助MVCC,數據庫可以實現READ COMMITTED,REPEATABLE READ等隔離級別,用戶可以查看當前數據的前一個或者前幾個歷史版本,保證了ACID中的I特性(隔離性)
?多版本并發控制僅僅是一種技術概念,并沒有統一的實現標準, 其的核心理念就是數據快照,不同的事務訪問不同版本的數據快照,從而實現不同的事務隔離級別。雖然字面上是說具有多個版本的數據快照,但這并不意味著數據庫必須拷貝數據,保存多份數據文件,這樣會浪費大量的存儲空間。InnoDB通過事務的undo日志巧妙地實現了多版本的數據快照。
?數據庫的事務有時需要進行回滾操作,這時就需要對之前的操作進行undo。因此,在對數據進行修改時,InnoDB會產生undo log。當事務需要進行回滾時,InnoDB可以利用這些undo log將數據回滾到修改之前的樣子。
?根據行為的不同 undo log 分為兩種 insert undo log和update undo log。
?insert undo log 是在 insert 操作中產生的 undo log。因為 insert 操作的記錄只對事務本身可見,對于其它事務此記錄是不可見的,所以 insert undo log 可以在事務提交后直接刪除而不需要進行 purge 操作。
?update undo log 是 update 或 delete 操作中產生的 undo log,因為會對已經存在的記錄產生影響,為了提供 MVCC機制,因此 update undo log 不能在事務提交時就進行刪除,而是將事務提交時放到入 history list 上,等待 purge 線程進行最后的刪除操作。
?為了保證事務并發操作時,在寫各自的undo log時不產生沖突,InnoDB采用回滾段的方式來維護undo log的并發寫入和持久化。回滾段實際上是一種 Undo 文件組織方式。
?InnoDB行記錄有三個隱藏字段:分別對應該行的rowid、事務號db_trx_id和回滾指針db_roll_ptr,其中db_trx_id表示最近修改的事務的id,db_roll_ptr指向回滾段中的undo log。如下圖所示。
?當事務2使用UPDATE語句修改該行數據時,會首先使用排他鎖鎖定改行,將該行當前的值復制到undo log中,然后再真正地修改當前行的值,最后填寫事務ID,使用回滾指針指向undo log中修改前的行。如下圖所示。
?當事務3進行修改與事務2的處理過程類似,如下圖所示。
?REPEATABLE READ隔離級別下事務開始后使用MVVC機制進行讀取時,會將當時活動的事務id記錄下來,記錄到Read View中。READ COMMITTED隔離級別下則是每次讀取時都創建一個新的Read View。
?Read View是InnoDB中用于判斷記錄可見性的數據結構,記錄了一些用于判斷可見性的屬性。
low_limit_id:某行記錄的db_trx_id < 該值,則該行對于當前Read View是一定可見的
up_limit_id:某行記錄的db_trx_id >= 該值,則該行對于當前read view是一定不可見的
low_limit_no:用于purge操作的判斷
rw_trx_ids:讀寫事務數組
?Read View創建后,事務再次進行讀操作時比較記錄的db_trx_id和Read View中的low_limit_id,up_limit_id和讀寫事務數組來判斷可見性。
?如果該行中的db_trx_id等于當前事務id,說明是事務內部發生的更改,直接返回該行數據。否則的話,如果db_trx_id小于up_limit_id,說明是事務開始前的修改,則該記錄對當前Read View是可見的,直接返回該行數據。
?如果db_trx_id大于或者等于low_limit_id,則該記錄對于該Read View一定是不可見的。如果db_trx_id位于[up_limit_id, low_limit_id)范圍內,需要在活躍讀寫事務數組(rw_trx_ids)中查找db_trx_id是否存在,如果存在,記錄對于當前Read View是不可見的。
?如果記錄對于Read View不可見,需要通過記錄的DB_ROLL_PTR指針遍歷undo log,構造對當前Read View可見版本數據。
?簡單來說,Read View記錄讀開始時及其之后,所有的活動事務,這些事務所做的修改對于Read View是不可見的。除此之外,所有其他的小于創建Read View的事務號的所有記錄均可見。
上述就是小編為大家分享的MySQL中InnoDB的一致性非鎖定讀是怎么樣的了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。