MySQL 事務實現原理


是什么是事務

事務是數據庫中一些列操作的集合,這個集合是按順序逐個執行的。在mysql中,保證數據具備ACID特性,這種特性使得事務使用起來非常安全和方便。比如銀行轉賬操作,使用事務就可以保證轉賬結果的正確,不同轉賬之間的隔離,轉賬過程中發生錯誤的回滾,以及機器崩潰的現場恢復。可以在99.99%的情況下保證我們數據安全。

事務具備ACID特性,分別是原子性、持久性、隔離性、一致性。
1、原子性指事務中的所有操作要么全部成功要么全部失敗,不能成功一半。
2、持久性指事務執行中機器崩潰后,重啟機器,能重新執行失敗的事務,保證事務不會因為某種情況被拋棄。
3、隔離性是針對事務之間的並發提出的概念,mysql做出了不同的隔離級別,隔離級別越高,事務並發性能越低,一般默認采用可重復讀級別。
4、一致性基於前三點特性所產生,即事務過程中無論發生什么,最終數據都是准確無誤的。

bin、redo、undo日志

每次update、insert、delete類型的SQL執行時都會先寫三條日志。

bin log記錄執行的每條SQL,用來做主從復制;
redo log記錄修改內容和修改頁的位置,用來維護持久性;
undo log記錄相反類型的SQL,用來做rollback和實現mvcc。

事務數據讀寫直接操作的都是緩存,由操作系統定時刷新到磁盤。log以追加寫的方式寫入緩存,一般mysql默認設置log寫入緩存立即刷新到磁盤,保證log數據不丟失。

隔離級別

臟讀:事務A還未提交修改,事務B讀取到的未提交數據是臟數據。(丟失更新:事務A修改記錄完成,但還未提交;事務B也修改了該記錄並提交,但是事務B由於其他原因回滾,導致事務A的修改失效。)
不可重復讀:事務A獲得查詢結果,在事務A提交前,事務B將某條數據修改並提交,事務A再次查詢,兩次查詢結果不同。
幻讀:和不可重復讀相同的場景,但幻讀專指數據的刪除或插入,而不是修改。

讀取已提交(解決1);可重復讀(解決1、2);串行化(全部解決)。

MVCC實現原理

mvcc是樂觀鎖的一種實現方式,目的是解決讀寫互斥問題,增大並發程度。

場景舉例:每次更新數據都為其記錄一個遞增的版本。線程A當第一次讀取數據時,版本號為1;此時該數據被線程B修改,那么版本號變為2;線程A再次讀取數據,發現版本號有變,此時可以根據不同的隔離級別來決定是否展示變更后的數據。

mysql中的mvcc通過隱藏列DB_TRX_ID、DB_ROLL_PTR和ReadView實現。DB_TRX_ID是該行記錄當前的事務id,DB_ROLL_PTR指向該行在undo log中的位置,ReadView是一個記錄當前活躍事務id的鏈表。

當一行記錄被修改時,undo文件中會存儲它的相反操作,並且存有修改之前的版本號。每次修改會在undo文件中形成鏈式存儲,鏈表頭部是最近的版本,尾部是最老版本。

當事務在執行select操作之前,會先構造ReadView,並通過ReadView判斷當前記錄是否是可見的。ReadView中按序存儲(事務id排序)着所有活躍事務id,如果當前記錄的事務id小於ReadView中的最小值,意味着該記錄已經被提交,則該記錄可見。如果大於ReadView中最大值,那么該記錄應當在之后的事務中提交,因此屬於不可見。如果處於最大最小值之前,則需要通過二分查找判斷,事務id是否存在於ReadView中,如果存在,說明該記錄的事務活躍,但無法判定事務是否已經提交,因此需要讀取DB_ROLL_PTR,獲取上一個版本的事務id,看看上個版本是否是可見的,通過層層遞歸,直到某一個之前的版本滿足可見的要求。

可重復讀實現原理

快照讀。
場景1:在事務A兩次查詢中間,事務B修改數據的場景下,第一次查詢的結果會作為快照保存,之后再次查詢就都是快照內容。

這是由於快照讀本質是mvcc實現的,因為事務A本身對數據操作,會導致事務A維護的版本號變更,再次查詢,事務A查詢的就是最新版本號數據。

mvcc在mysql中,通過隱藏字段DB_TRX_ID、DB_ROLL_PTR和undo log實現。DB_TRX_ID記錄該行記錄目前的版本號,DB_ROLL_PTR記錄該行之前版本在undo log中的位置。那么在上述場景1下,事務A通過DB_ROLL_PTR,可以直接獲取被事務B修改之前的數據。

場景2:如果在事務B插入數據1之后,事務A也修改了數據1,那么事務A再次查詢就會看到本不存在的數據1。可見不可重復讀無法解決幻讀。

若想解決幻讀,需要使用mvcc+NextKey Locks(間隙鎖+行鎖)。

我們都知道InnoDB支持行鎖,並且行鎖是鎖住索引。而間隙鎖用來鎖定索引記錄間隙,確保索引記錄的間隙不變。間隙鎖是針對事務隔離級別為Repeatable Read或以上級別而已的,間隙鎖和行鎖一起組成了Next-Key Lock。當InnoDB掃描索引記錄的時候,會首先對索引記錄加上行鎖,再對索引記錄兩邊的間隙加上間隙鎖(Gap Lock)。加上間隙鎖之后,其他事務就不能在這個間隙插入記錄。這樣就有效的防止了幻讀的發生。
默認情況下,InnoDB工作在Repeatable Read的隔離級別下,並且以Next-Key Lock的方式對索引行進行加鎖。當查詢的索引具有唯一性(主鍵、唯一索引)時,Innodb存儲引擎會對Next-Key Lock進行優化,將其降為行鎖,僅僅鎖住索引本身,而不是范圍(除非鎖定不存在的值)。若是普通索引,則會使用Next-Key Lock將記錄和間隙一起鎖定。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM