MySql系列 MySQL 到底是怎麼解決幻讀的?

2021-09-23 20:45:54 字數 3105 閱讀 9189

什麼是幻讀?在一次事務裡面,多次查詢之後,結果集的個數不一致的情況叫做幻讀。而多出來或者少的哪一行被叫做幻行。

為什麼要解決幻讀?在高併發資料庫系統中,需要保證事務與事務之間的隔離性,還有事務本身的一致性。

mysql 是如何解決幻讀的?如果你看到了這篇文章,那麼我會預設你了解了髒讀 、不可重複讀與可重複讀。

不可重複讀(鎖行即可解決)和幻讀的區別(需要鎖表解決)

很多人容易搞混不可重複讀(不可重複讀重點是在update,即事務前後對比特定資料內容的修改;而幻讀是insert和delete,即事務前後資料條數的對比)和幻讀,確實這兩者有些相似。

所以說不可重複讀和幻讀最大的區別,就在於如何通過鎖機制來解決他們產生的問題。

但是mysql、oracle、postgresql等成熟的資料庫,出於效能考慮,都是使用以樂觀鎖為理論基礎的mvcc(多版本併發控制)來避免這兩種問題。

悲觀鎖和樂觀鎖

正如其名,它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個資料處理過程中,將資料處 於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制(也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機 制,也無法保證外部系統不會修改資料)。

在悲觀鎖的情況下,為了保證事務的隔離性,就需要一致性鎖定讀。讀取資料時給加鎖,其它事務無法修改這些資料。修改刪除資料時也要加鎖,其它事務無法讀取這些資料。

相對悲觀鎖而言,樂觀鎖機制採取了更加寬鬆的加鎖機制。悲觀鎖大多數情況下依靠資料庫的鎖機制實現,以保證操作最大程度的獨占性。但隨之而來的就是資料庫效能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。

而樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基於資料版本( version )記錄機制實現。何謂資料版本?即為資料增加乙個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表增加乙個 「version」 欄位來實現。讀取出資料時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交資料的版本資料與資料庫表對應記錄的當前版本資訊進行比對,如 果提交的資料版本號大於資料庫表當前版本號,則予以更新,否則認為是過期資料。

要說明的是,mvcc的實現沒有固定的規範,每個資料庫都會有不同的實現方式,這裡討論的是innodb的mvcc。

1. 多版本併發控制(mvcc)(快照讀/一致性讀)

多數資料庫都實現了多版本併發控制,並且都是靠儲存資料快照來實現的。以 innodb 為例,每一行中都冗餘了兩個字斷。

乙個是行的建立版本,乙個是行的刪除(過期)版本。具體的版本號(trx_id)存在 information_schema.innodb_trx 表中。版本號(trx_id)隨著每次事務的開啟自增。

事務每次取資料的時候都會取建立版本小於當前事務版本的資料,以及過期版本大於當前版本的資料。

普通的 select 就是快照讀。

select id from t where number = 1;
原理:將歷史資料存乙份快照,所以其他事務增加與刪除資料,對於當前事務來說是不可見的。

2. next-key 鎖 (當前讀)

next-key 鎖包含兩部分:

記錄鎖是加在索引上的鎖,間隙鎖是加在索引之間的。(思考:如果列上沒有索引會發生什麼?)

select id from t where number = 1 for update;

select id from t where number = 1 lock in share mode;

insert

update

delete

其他:mysql innodb 引擎 rr 隔離級別是否解決了幻讀

mysql官方給出的幻讀解釋是:只要在乙個事務中,第二次select多出了row就算幻讀。

a事務先select,b事務insert確實會加乙個gap鎖,但是如果b事務commit,這個gap鎖就會釋放(釋放後a事務可以隨意dml操作),a事務再select出來的結果在mvcc下還和第一次select一樣,接著a事務不加條件地update,這個update會作用在所有行上(包括b事務新加的),a事務再次select就會出現b事務中的新行,並且這個新行已經被update修改了,實測在rr級別下確實如此。

如果這樣理解的話,mysql的rr級別確實防不住幻讀

在快照讀讀情況下,mysql通過mvcc來避免幻讀。 在當前讀讀情況下,mysql通過next-key來避免幻讀。 select *

from t where a=1;屬於快照讀 select * from t where a=1 lock in share

mode;屬於當前讀

不能把快照讀和當前讀得到的結果不一樣這種情況認為是幻讀,這是兩種不同的使用。所以我認為mysql的rr級別是解決了幻讀的。

如引用一問題所說,t1 select 之後 update,會將 t2 中 insert 的資料一起更新,那麼認為多出來一行,所以防不住幻讀。看著說法無懈可擊,但是其實是錯誤的,innodb 中設定了快照讀和當前讀兩種模式,如果只有快照讀,那麼自然沒有幻讀問題,但是如果將語句提公升到當前讀,那麼 t1 在 select 的時候需要用如下語法: select * from t for update (lock in share mode) 進入當前讀,那麼自然沒有 t2 可以插入資料這一回事兒了。

next-key 固然很好的解決了幻讀問題,但是還是遵循一般的定律,隔離級別越高,併發越低。

到這裡mysql系列:mysql 到底是怎麼解決幻讀的?分享完畢了,快去試試吧!下一波將分享=>tcp系列:tcp 乾貨三次握手和四次揮手的一些問題!

-------

mysql到底是怎麼解決幻讀的?

mysql到底是怎麼解決幻讀的?mvcc是什麼,怎麼實現的?先理解幾個概念 非鎖定的一致性讀 圖中左邊為非一致性讀部分 非一致性讀 在sql查詢的時候,如果發現記錄已經被加了x鎖,會轉而查詢當前記錄回滾段中最近的快照,讀快照不加鎖,非常快 mvcc 多版本控制 這裡的多版本就是指的回滾段的快照,用來...

MySQL 到底是怎麼解決幻讀的?

在一次事務裡面,多次查詢之後,結果集的個數不一致的情況叫做幻讀。而多出來或者少的哪一行被叫做幻行。在高併發資料庫系統中,需要保證事務與事務之間的隔離性,還有事務本身的一致性。如果你看到了這篇文章,那麼我會預設你了解了髒讀 不可重複讀與可重複讀。多數資料庫都實現了多版本併發控制,並且都是靠儲存資料快照...

this到底是誰

js中函式的4種呼叫方式 1.作為普通函式來呼叫 alert window.xx undefined function t t alert window.xx 333 解釋 作為普通函式來呼叫this時,this的值指向 windwo,準確的說,this為null,但被解釋成window,在ecma...