1. 程式人生 > >讀《MySQL實戰45講》第八講總結

讀《MySQL實戰45講》第八講總結

事物訊息如何實現MVCC(多版本併發控制)

  • 每個事務在啟動時,都會向InnoDB申請一個id,稱為transaction id,這個id是唯一的,並且是按申請順序嚴格遞增的。
  • 資料表中每一行是多版本的,也就是說對於同一行,每次事務更新都會生成一個新的版本,這個版本就記為row trx_id,這個row trx_id的值其實就是最新一次更新的事務的transaction id。而且舊版本是會保留的。
  • 事物在啟動時,會找到所有已經提交的事務的最大transaction id作為up_limit_id,具體實現上,InnoDB同時還會為事務構造一個數組,用來儲存所有在事務啟動之前已經啟動了但未提交的的事務的transaction id。
  • 事務在更新一條語句的時候,比如對ID = 1 的行,將其某一列的值c由3改為4,它會先將c = 3的時候該行的row trx_id儲存到undo log(也就是實現了舊版本是會保留的這一概念)。然後在資料頁將c由3改為4,最後將自己的row trx_id記在該行的行頭。
  • 如果事務是要查詢的話,它需要先將自己的up_limit_id與要查詢的那一行的row trx_id作比較,如果up_limit_id < row trx_id,則需要到unlog裡去查詢up_limit_id >= row trx_id時的版本記錄。
  • 重點記住:可重複讀的隔離條件下,事務啟動以前的提交是可見的,事務啟動後的提交是不可見的。
  • 例子:事務A的transaction id是100,在A啟動之前,transaction id為90的事務提交了一次更新,row trx_id變為90,即已經提交了的事務的最大transaction id是90,就是說up_limit_id值為90。此外,系統裡還有一個活躍(啟動但未提交)的事務X,transaction id為99,此時A記錄的陣列就是[99,100]。在A啟動後,如果有事務B(transaction id = 88)提交了一次更新,row trx_id變為88,然後事務A執行了一次查詢,此時A查詢到的值是row trx_id為90的版本對應的值。因為雖然88 < 90,也即up_limit_id > row trx_id,但是B不在陣列[99,100]中,也就是B是在A啟動後提交的,對於A是不可見的。
  • 重點記住:事務更新時基於當前讀的,就是更新的時候讀取的資料一定是最新的資料,否則會導致之前的更新丟失。
  • 例子:事務C記錄的陣列為[99,100,101],事務D記錄的陣列為[99,100,101,102],兩個事務的p_limit_id值均為90,事務D先執行一次更新並且立馬提交,此時row trx_id = 102,那麼之後事務C要更新時,讀取到的值是row trx_id = 102的版本的值。如果事物D執行更新後沒有馬上提交哦啊,事物C也想更新,此時事務C無法執行更新,因為基於兩階段鎖協議,事務D未提交,也就是寫鎖未釋放,而事務C是當前讀,而且也需要寫鎖,所以事務C需要等到事務D提交釋放寫鎖後才能更新。
  • 重點記住:可重複讀隔離與讀提交隔離的不同在於檢視建立時間不同,可重複讀隔離下的事務的檢視創建於事務啟動的時候,而讀提交隔離下的事務的檢視創建於語句執行之前,而且每一個新語句執行前會新建立一個檢視

參考

《MySQL實戰45講》第八講:事務到底是隔離的還是不隔離的?