成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

幻讀:聽說我是被MVCC干掉的?

原創
運維 數據庫運維 開發工具
我是幻讀,聽說有人認為我是 MVCC 解決的,為了讓大家更全面地理解我,只能親自來解釋一下。

【51CTO.com原創稿件】我是幻讀,聽說有人認為我是 MVCC 解決的,為了讓大家更全面地理解我,只能親自來解釋一下。

[[396015]]

圖片來自 Pexels

我是誰?

先給大家做一個簡單的自我介紹,我就是事務并發時會產生的三大問題之一。

我的其他倆兄弟臟讀、不可重復讀被 MVCC 在上一個回合無情的干掉了,至于上個回合發生了什么可以去看劇情回顧。

我的由來就是因為主人在操作一組數據時還有很多人也在對這組數據進行操作。

舉一個簡單的案例:根據條件在對一組數據進行過濾返回的結果為 100 個,但是在主人操作的同時其他人又新增了符合條件的數據,然后主人再次進行查詢時返回結果為 101。第二次返回的數據跟第一次返回數據不一致。

于是我誕生了,大家還給我起了個很好聽的名字幻讀。為什么會給我起這個名字呢!那是因為我給人們的現象好像出了幻覺一樣。

為什么有人會認為我是被 MVCC 干掉的

為了演示方便,就直接使用之前的測試表來進行操作。

同時大家可以看到此表還有一些測試數據,一切從頭開始,清空表。

清空表的命令:

  1. truncate table_name 

執行這個命令會使表的數據清空,并且自增 ID 會從 1 開始。

從執行過程來看,truncate table 類似于 drop table 然后在 create table,這里的環境都是測試環境,千萬不要在線上進行操作,因為它繞過了 DML 方法,是不能回滾的。

進行了一點小插曲,進入正題。

根據上圖的執行步驟,預期來說左邊事務的第一條 select 語句查詢結果為空。第二個 select 查詢結果為 1 條數據,包含右邊事務提交的數據。

但在實際測試的情況下,第一次執行 select 和第二次執行 select 返回結果一致。

從這個案例中,可以得出結論確實在不可重復隔離級別下會解決幻讀問題(在快照讀的前提下)。

我真的是被 MVCC 解決的?

通過上述測試案例來看,貌似在 MySQL 中通過 MVCC 就解決我的引來的問題,那既然都解決了我的問題,為什么還有串行化的隔離級別呢!好疑惑啊!

帶著這個疑問繼續進行實驗,為了方便就不再使用上邊表結構了,建立一個簡單的表結構。

再進入一個小插曲你知道在 MySQL 終端如何清屏嗎?執行命令 system clear 即可。

接著開始新一輪的測試:

上圖案例事務 1 幾次查詢數據都是空。此時事務 2 已經成功將數據插入并且提交。但當事務 1 幾次查詢數據為空之后進行數據插入時,提示主鍵重復。

再來看一個案例:

如上圖:

  • step1:事務 1 開啟事務
  • step2:事務 2 開啟事務
  • step3:事務 1 查詢數據只有一條數據
  • step4:事務 2 添加一條數據
  • step5:事務 1 查詢數據為一條
  • step6:事務 2 提交事務
  • step7:事務 1 查詢數據為一條
  • step8:事務 1 修改 name
  • step9:猜想一下此時表內數據會發生什么改變

此案例中事務 1 始終讀取數據都是一條數據,但是在修改數據時影響數據行數卻是 2,再次進行查看數據時竟然出現了事務 2 添加的數據。這也可以看作是一種幻讀。

小結:通過以上倆個案例得知在 MySQL 可重復讀隔離級別中并沒有完全解決幻讀問題,而只是解決了快照讀下的幻讀問題。

而對于當前讀的操作依然存在幻讀問題,也就是說 MVCC 對于幻讀的解決是不徹底的。

再聊當前讀、快照讀

在上一回合中快照讀、當前讀已經被消化了,為了防止消化不良這里再簡單說明一下。

①當前讀

所有操作都加了鎖,并且鎖之間除了共享鎖都是互斥的,如果想要增、刪、改、查時都需要等待鎖釋放才可以,所以讀取的數據都是最新的記錄。

簡單來說,當前讀就是加了鎖的,增、刪、改、查,不管鎖是共享鎖、排它鎖均為當前讀。

在 MySQL 的 Innodb 存儲引擎下,增、刪、改操作都會默認加上鎖,所以增、刪、改操作默認就為當前讀。

②快照讀

快照讀的出現旨在提高事務并發性,實現基于我的敵人 MVCC,簡單來說快照讀就是不加鎖的非阻塞讀,即簡單的 select 操作(select * from user)。

在 Innodb 存儲引擎下執行簡單的 select 操作時,會記錄下當前的快照讀數據,之后的 select 會沿用第一次快照讀的數據,即使有其它事務提交也不會影響當前的 select 結果,這就解決了不可重復讀問題。

快照讀讀取的數據雖然是一致的,但有可能不是最新的數據而是歷史數據。

告訴你們吧!當前讀的情況下,我是被 next-key locks 干掉的

第二小節中得知在快照讀下由于我引發的問題已經被 MVCC 消滅了。但是在小節三進行案例測試發現在當前讀下我又滿血復活了。

我要是那么容易被干掉還怎么被稱為打不死的小強,這不是鬧笑話呢!說歸說,鬧歸鬧如果 MVCC 把它的小弟 next-key locks 帶上那我就完了,就不再像灰太狼說經典語錄“我一定會回來的”。

此時就要思考一個問題,在 Innodb 存儲引擎下,是默認給快照讀加 next-key locks,還是說需要手動加鎖。

通過官方文檔對于 next-key locks 的解釋:

To prevent phantoms, InnoDB uses an algorithm called next-key locking that combines index-row locking with gap locking. InnoDB performs row-level locking in such a way that when it searches or scans a table index, it sets shared or exclusive locks on the index records it encounters. Thus, the row-level locks are actually index-record locks. In addition, a next-key lock on an index record also affects the “gap” before that index record. That is, a next-key lock is an index-record lock plus a gap lock on the gap preceding the index record. If one session has a shared or exclusive lock on record R in an index, another session cannot insert a new index record in the gap immediately before R in the index order.

大致意思,為了防止幻讀,Innodb 使用 next-key lock 算法,將行鎖(record lock)和間隙鎖(gap lock)結合在一起。

Innodb 行鎖在搜索或者掃描表索引時,會在遇到的索引記錄上設置共享鎖或者排它鎖,因此行鎖實際是索引記錄鎖。

另外, 在索引記錄上設置的鎖同樣會影響索引記錄之前的“間隙(gap)”。即 next-key lock 是索引記錄行加上索引記錄之前的“gap”上的間隙鎖定。

并且還給了一個案例:

  1. SELECT * FROM child WHERE id > 100 FOR UPDATE

當 Innodb 掃描索引時,會將 id 大于 100 地上鎖,阻止任何大于 100 的數據添加。

到這里就回答了上邊問題,在 Innodb 下解決當前讀產生的幻讀問題需要手動加鎖來解決。

再來看一個案例,下圖為此時的數據情況:

下圖的這個案例就解決了在第三節中第一個案例的幻讀問題。

如上圖:

  • step事務1:開啟事務
  • step事務2:開啟事務
  • step事務1:查詢 ID 為 4 的這條數據并且加上排它鎖
  • step事務2:添加 ID 為 4 的數據,并且等待事務 1 釋放鎖
  • step事務1:添加 ID 為 4 的數據,添加成功
  • step事務1:查詢當前數據
  • step事務1:提交事務
  • step事務2:報錯,返回主鍵重復問題

這個案例查詢的索引列是主鍵并且是唯一的,此時 Innodb 引擎會對 next-key lock 做降級處理,也就是只鎖定當前查詢的索引記錄行,而不是范圍鎖定。

案例二:還是使用上邊的數據,但是這次我們進行一次范圍查找。

此時的數據為 1,3,5,查找的范圍為大于 3。從下圖可以看出當事務 2 執行添加 ID 為 2 的是可以添加成功的。

但是當添加 ID 6 時需要等待。此時若事務 1 不提交事務,事務 2 添加 ID 為 6 的這條數據就執行不成功。

對于上述的 SQL 語句 select * from user where id > 3 for update;執行返回的只有 5 這一行數據。

此時鎖定的范圍為 (3,5],(5,∞),所以說 id 為 2 的可以插入,ID 為 4 或者大于 5 的都是插入不了的。

以上就是在 Innodb 中解決幻讀問題最終方案。

幻讀解決方案

為了方便大家直觀了解幻讀的解決方案,這里咔咔進行簡單的總結。

通過 MVCC 解決了快照讀下的幻讀問題,為什么能解決?在第一次執行簡單的 select 語句就生成了一個快照,并且在后邊的 select 查詢都是沿用第一次快照讀的結果。所以說快照讀查詢到的數據有可能是歷史數據。

通過 next-key lock 解決當前讀的幻讀問題,next-key lock 是 record lock 和 gap lock 的結合,鎖定的是一個范圍,如果查詢數據為索引記錄行,則只會鎖定當前行,也就是說降級為 record lock。

若為范圍查找時就會鎖定一個范圍,例如上例中 ID 為 1,3,5 查詢大于 3 的數據,則會把 (3,5],(5,∞) 進行范圍鎖定,其它事務在鎖未釋放之前是無法插入的。

從官方文檔還可得知如果需要驗證數據唯一性只需要給查詢加上共享鎖即可,也就是給 select 語句加上 in lock share mode,如果返回結果為空,則可以進行插入,并且插入的這個值肯定是唯一的。

同樣也可以添加 next key lock 防止其他人同時插入相同數據,小節 5 的所有案例就是使用的 next-key lock,從這一點可以得知 next-key lock 是可以鎖定表內不存在的索引。

根據上述結論來看,如果想要檢測數據唯一性使用共享鎖,那么多個事務同時開啟共享鎖,又同時添加相同的數據怎么辦,會不會出現問題呢?

明確地說明是不會的,如果多個事務同時插入相同數據只會有一個事務添加成功,其它事務會拋出錯誤,這個就是一個新的概念“死鎖”。

擴展

事務 ID 是在何時分配的?在本文或者其它資料中都能得到一個信息就是當執行一條簡單的 select 語句同時也會生成 read-view。

雖然快照讀、read-view 都是基于事務啟動的前提下,但是 read-veiw 是通過未提交事務 ID 組成的。

①那么到底是在何時分配事務 ID 的呢?

事務的啟動方式有兩種,分別為顯示啟動、另一種是設置 autocommit=0 后執行 select 就會啟動事務。

在顯示啟動中最簡單的就是以 begin 語句開始,也可以使用 start transaction 開啟事務。

若使用 start trancaction 開啟事務也可以選擇開始只讀事務還是讀寫事務。

看了很多資料都說當開啟一個事務時會分配一個事務 ID,那么來驗證一下是這個樣子的嗎?

通過上圖可以看到當執行一個 begin 語句之后查詢事務 ID 是空的,也就說當執行 begin 后并沒有分配 trx_id。

那么當執行 begin 后在支持 DML 語句呢!

根據文檔得知,執行 begin 命令并不是真正開啟一個事務,僅僅是為當前線程設定標記,表示為顯式開啟的事務。

所以要明白對數據進行了增、刪、改、查等操作后才算真正開啟了一個事務,此時會去引擎層開啟事務。

②為什么事務 ID 差異特別大?

上圖中查詢了當前活躍的事務 ID,但是兩個事務 ID 的差異特別大。相信很多小伙伴都遇到過這個問題,有問題不害怕,害怕的是沒有問題。

事實上在這兩條數據中只有 20841 是真正的事務 ID,那么第二條數據中的 ID 是什么呢!

想知道這個數字是什么的前提是知道是怎么來的。

從上圖可以看出,當執行 select 語句后會產生一個非常大的事務 ID,那能不能理解為這種差異非常大的事務 ID 是通過快照讀的方式才會生成的。

接著再這個事務下面在執行一個 insert 語句,然后再查看一下事務 ID 的狀態。

不可思議的是在事務中先執行 select 語句,然后執行 insert 語句,事務 ID 發生了變化,這是什么原因呢?

經過資料查詢得知當執行一個簡單的 select 語句時,被稱之為只讀事務,為了避免給只讀事務分配 trx_id 帶來不必要的開銷就沒有對其分配事務 ID。

只讀事務沒有分配 undo segment 也不會分配 LOCK 鎖結構,本質上只讀事務的 trx_id 的值就是 0。

但是為了執行 select * from information_schema.INNODB_TRX 或者 show engine innodb status 時。

就會通過 reinterpret_cast(trx) | (max_trx_id + 1) 將指針轉換為一個 64 字節非負整數然后位或 (max_trx_id + 1) 就是這么個值。

關于這個值的生成過程就不用再去深究了,只需要知道在只讀事務下是不會分配事務 ID,而查詢出來的這個值只是為了顯示而存在的沒有實際意義。

但是當你執行 select * from information_schema.INNODB_TRX 查詢出來的事務 ID,再通過 show engine innodb status 查詢是查不到的。

在 Innodb 下如果事務為只讀事務則不會在 Innodb 數據結構中顯示,因此你是看不到的。

作者:咔咔

簡介:堅持學習、堅持寫博、堅持分享是咔咔從業以來一直所秉持的信念。希望在偌大互聯網中咔咔的文章能帶給你一絲絲幫助。我是咔咔,下期見。

編輯:陶家龍 

征稿:有投稿、尋求報道意向技術人請添加小編微信 gordonlonglong

【51CTO原創稿件,合作站點轉載請注明原文作者和出處為51CTO.com】

 

責任編輯:武曉燕 來源: 51CTO技術棧
相關推薦

2023-08-09 17:22:30

MVCCMySQL數據

2019-05-28 13:50:27

MySQL幻讀數據庫

2022-10-19 11:17:35

2022-09-08 13:56:49

MySQL事務記錄鎖

2022-06-30 08:00:00

MySQL關系數據庫開發

2022-02-17 21:13:08

MySQL架構體系

2021-04-20 19:21:50

臟讀MySQL幻讀

2024-04-19 08:18:47

MySQLSQL隔離

2021-04-20 08:02:08

業務數據用戶

2024-09-02 00:00:00

MySQL幻讀數據

2021-02-23 09:06:00

MVCC版本并發

2024-03-11 00:00:00

mysqlInnoDB幻讀

2024-04-24 08:26:35

事務數據InnoDB

2024-04-25 08:16:06

InnodbReadMVCC

2022-03-29 10:52:08

MySQL數據庫

2020-04-09 08:29:50

編程語言事件驅動

2021-12-02 08:19:06

MVCC面試數據庫

2019-03-21 09:06:00

數據庫復讀幻讀

2022-04-27 07:32:02

臟讀幻讀不可重復讀

2016-09-30 01:04:45

數據分析數據
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91资源在线 | a国产视频 | japan25hdxxxx日本 做a的各种视频 | 午夜精品久久久久久久99黑人 | 九九亚洲 | 精品免费国产一区二区三区四区介绍 | 亚洲欧美中文日韩在线 | 超碰97在线免费 | 不卡在线视频 | 密色视频 | 国产精品久久久久久久久久久久午夜片 | 午夜一区 | 最新中文字幕第一页视频 | 亚洲3级| 一级在线 | 国产精品一区视频 | 精品欧美黑人一区二区三区 | 在线综合视频 | 日韩高清成人 | 欧美一级淫片免费视频黄 | 日本久久久一区二区三区 | 成人三级影院 | 欧美精品成人一区二区三区四区 | 中文字幕91 | 欧美精品综合 | 在线免费看黄 | 中文字幕av一区二区三区 | 亚洲一区久久 | 免费观看黄a一级视频 | 91精品在线播放 | 玖玖玖在线观看 | 国产精品无码久久久久 | 亚洲 精品 综合 精品 自拍 | 亚洲网站在线观看 | 中文字幕精品一区 | 亚洲精品一区二区网址 | 精品视频在线观看 | 日本黄色短片 | 成人欧美一区二区三区在线播放 | 精品一区二区三区入口 | 人人干人人超 |