事務隔離級別讀書筆記分享

wei-xh發表於2018-04-25
  1. 在MVCC併發控制中,讀操作可以分成兩類:快照讀 (snapshot read)與當前讀 (current read)。快照讀,某一時刻的一致性讀,不用加鎖。當前讀,讀取的是記錄的最新版本,並且,當前讀返回的記錄,都會加上鎖,保證其他事務不會再併發修改這條記錄。

  2. MySQL在RR隔離級別下,快照讀和當前讀如果在一個會話中先後出現,可能會出現幻讀。因為快照讀不加鎖,會允許新的插入,當前讀需要讀到塊的最新版本,因此快照讀和當前讀兩次操作間,就可能會出現幻讀。 https://www.cnblogs.com/crazylqy/p/7612230.html
  3. MySQL為實現RR隔離級別,帶來了很大的代價,引入了Next-Key Locking解決當前讀模式下的幻讀問題。Next-Key Locking可能會導致大量的DML失敗。 https://www.cnblogs.com/crazylqy/p/7689447.html,Oracle沒有RR隔離級別,在read only級別下,Oracle沒有幻讀的問題(是快照讀的模式),在read committed級別下,快照讀、當前讀以及混合的【快照讀和當前讀】下都存在幻讀的問題。 https://www.cnblogs.com/crazylqy/p/7614092.html
  4. RR隔離級別,MySQL是依靠MVCC實現的可重複讀(read view),同時依靠MVCC實現快照讀下的幻讀問題,依靠Next-Key Locking實現當前讀下的幻讀問題,所以MySQL InnoDB的可重複讀並不保證避免幻讀,需要應用顯式的使用加鎖讀來保證,而這個加鎖讀使用到的機制就是next-key locks。
  5. 髒讀、不可重複讀、幻讀,是一種缺陷,越往後,解決缺陷的成本越高
  6. 隔離級別越高,能解決的“缺陷”越多,為什麼不直接使用最高的事務隔離級別,那不就沒有缺陷了? 因為隔離級別越高,併發性可能會越低。
  7. 髒讀,解決 寫不阻塞讀的問題,提高併發性,犧牲讀一致性。現在絕大多數的主流資料庫,都是透過MVCC來解決寫不阻塞讀的問題,不是透過經典的鎖來實現。
  8. 為什麼會出現不可重複讀取? 
    在經典的read commited方式下,對於讀取過的資料並不加鎖(讀取的當下加共享鎖,讀取完成後釋放共享鎖),那麼再次訪問時資料可能已經被其他事務修改。
  9. 如何才能做到可重複讀? 
    老一輩的資料庫藝術家用的方式是加鎖,對讀取過的資料加鎖,就能保證每一次讀取到的資料都是一樣的,因為對讀取過的資料加鎖後,資料無法發生修改了。這也是repeatable read這個隔離級別要解決的問題,但是經典的實現可重複讀的方式會產生幻讀。現在絕大多數的主流資料庫,是透過MVCC來做到的可重複讀,不是透過加鎖。
  10. 經典的可重複讀提供了一個一致性(語句級和事務級)的讀取方式,雖存在幻讀,單從一致性的角度看,並不是一個大的缺陷,如果以經典的鎖的方式去實現可重複讀,發生死鎖的機率極大,但帶來的一個(好的)副作用,解決了丟失更新的問題。
  11. 按照經典的隔離級別定義,read uncommited,read commited,都不能提供一致性讀。因為當下主流資料庫都基於MVCC實現,不基於經典的鎖方式,所以都實現了在read commited級別下的語句級的一致性。經典的隔離級別下,repeatable read在語句級一致性的基礎上還做到了事務級的一致性。
  12. 針對oracle的隔離級別來說,read commited級別能夠提供語句級的一致性,這個隔離級別避免不了事務級的幻讀的問題,需要read only或者最高的SERIALIZABLE級別。
  13. 在一個採用共享讀鎖(而不是多版本)的資料庫中,如果啟用了REPEATABLE READ,可以避免丟失更新的問題。原因是:已被讀取的資料會在上面加一個鎖(共享讀鎖,非排它鎖),這個鎖會保證資料不能被任何其他事務修改。
  14. 在可重複讀(REPEATABLE READ,簡稱RR)隔離級別下,read view是在第一個讀請求發起時建立的。在讀已提交(READ COMMITTED,簡稱RC)隔離級別下,則是在每次讀請求時都會重新建立一份read view。根據上面提到的說法,RC隔離級別下,是每次發起SELECT都會建立read view,也就是每次SELECT都能讀取到本次查詢開始時的已經commit的資料,所以才會出現不可重複讀、幻讀現象。
  15. read view 判斷當前版本資料項是否可見 
    在innodb中,建立一個新事務的時候,innodb會將當前系統中的活躍事務列表(trx_sys->trx_list)建立一個副本(read view),副本中儲存的是系統當前不應該被本事務看到的其他事務id列表。當使用者在這個事務中要讀取該行記錄的時候,innodb會將該行當前的版本號與該read view進行比較。 
    具體的演算法如下: 
    1. 設該行的當前事務id為trx_id_0,read view中最早的事務id為trx_id_1, 最遲的事務id為trx_id_2.
    2. 如果trx_id_0< trx_id_1的話,那麼表明該行記錄所在的事務已經在本次新事務建立之前就提交了,所以該行記錄的當前值是可見的。跳到步驟6.
    3. 如果trx_id_0>trx_id_2的話,那麼表明該行記錄所在的事務在本次新事務建立之後才開啟,所以該行記錄的當前值不可見.跳到步驟5。
    4. 如果trx_id_1<=trx_id_0<=trx_id_2, 那麼表明該行記錄所在事務在本次新事務建立的時候處於活動狀態,從trx_id_1到trx_id_2進行遍歷,如果trx_id_0等於他們之中的某個事務id的話,那麼不可見。跳到步驟5.
    5. 從該行記錄的DB_ROLL_PTR指標所指向的回滾段中取出最新的undo-log的版本號,將它賦值該trx_id_0,然後跳到步驟2.
    6. 將該可見行的值返回。

需要注意的是,新建事務(當前事務)與正在記憶體中commit 的事務不在活躍事務連結串列中。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22034023/viewspace-2153328/,如需轉載,請註明出處,否則將追究法律責任。

相關文章