国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

多版本并發(fā)控制MVCC

這篇具有很好參考價(jià)值的文章主要介紹了多版本并發(fā)控制MVCC。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

什么是MVCC

MVCC (Multiversion Concurrency Control),多版本并發(fā)控制。顧名思義,MVCC 是通過數(shù)據(jù)行的多個(gè)版本管理來實(shí)現(xiàn)數(shù)據(jù)庫的并發(fā)控制。這項(xiàng)技術(shù)使得在InnoDB的事務(wù)隔離級(jí)別下執(zhí)行一致性讀操作有了保證。換言之,就是為了查詢一些正在被另一個(gè)事務(wù)更新的行,并且可以看到它們被更新之前的值,這樣在做查詢的時(shí)候就不用等待另一個(gè)事務(wù)釋放鎖。

快照讀與當(dāng)前讀

MVCC在MySQL InnoDB中的實(shí)現(xiàn)主要是為了提高數(shù)據(jù)庫并發(fā)性能,用更好的方式去處理讀-寫沖突,做到即使有讀寫沖突時(shí),也能做到不加鎖,非阻塞并發(fā)讀,而這個(gè)讀指的就是快照讀, 而非當(dāng)前讀。當(dāng)前讀實(shí)際上是一種加鎖的操作,是悲觀鎖的實(shí)現(xiàn)。而MVCC本質(zhì)是采用樂觀鎖思想的一種方式。

快照讀

快照讀又叫一致性讀,讀取的是快照數(shù)據(jù)。 不加鎖的簡(jiǎn)單的 SELECT 都屬于快照讀 ,即不加鎖的非阻塞讀;比如這樣:

select * from table where ...

之所以出現(xiàn)快照讀的情況,是基于提高并發(fā)性能的考慮,快照讀的實(shí)現(xiàn)是基于MVCC,它在很多情況下,避免了加鎖操作,降低了開銷。
既然是基于多版本,那么快照讀可能讀到的并不一定是數(shù)據(jù)的最新版本,而有可能是之前的歷史版本。
快照讀的前提是隔離級(jí)別不是串行級(jí)別,串行級(jí)別下的快照讀會(huì)退化成當(dāng)前讀。

當(dāng)前讀

當(dāng)前讀讀取的是記錄的最新版本(最新數(shù)據(jù),而不是歷史版本的數(shù)據(jù)),讀取時(shí)還要保證其他并發(fā)事務(wù)不能修改當(dāng)前記錄,會(huì)對(duì)讀取的記錄進(jìn)行加鎖。加鎖的 SELECT,或者對(duì)數(shù)據(jù)進(jìn)行增刪改都會(huì)進(jìn)行當(dāng)前讀。比如:

select * from student lock in share mode; # 共享鎖,S鎖

select * from student for update; # 排他鎖,X鎖

insert into student values ... # X鎖

delete from student where ... # X鎖

update student set ... # X鎖

前置知識(shí)

隔離級(jí)別

我們知道事務(wù)有4個(gè)隔離級(jí)別,存在3種并發(fā)問題。
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC
在MySQL中,默認(rèn)的隔離級(jí)別是可重復(fù)讀,可以解決臟讀和不可重復(fù)讀的問題,如果僅從定義的角度來看,它并不能解決幻讀問題。如果我們想要解決幻讀問題,就需要采用串行化的方式,也就是將隔離級(jí)別提升到最高,但這樣一來就會(huì)大幅降低數(shù)據(jù)庫的事務(wù)并發(fā)能力。
而MVCC 可以不采用鎖機(jī)制,而是通過樂觀鎖的方式來解決不可重復(fù)讀和幻讀問題!它可以在大多數(shù)情況下替代行級(jí)鎖,降低系統(tǒng)的開銷。
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC

隱藏字段,undo log版本鏈

回顧一下undo日志的版本鏈,對(duì)于使用InnoDB存儲(chǔ)引擎的表來說,它的聚簇索引記錄中都包含兩個(gè)必要的隱藏列(字段)。

  • trx_id:每次一個(gè)事務(wù)對(duì)某條聚簇索引記錄進(jìn)行改動(dòng)時(shí),都會(huì)把該事務(wù)的事務(wù)id賦值給trx_id隱藏列。
  • roll_pointer:每次對(duì)某條聚簇索引記錄進(jìn)行改動(dòng)時(shí),都會(huì)把舊的版本寫入到undo日志中,然后這個(gè)隱藏列就相當(dāng)于一個(gè)指針,可以通過它來找到該記錄修改前的信息。

舉例:student表的數(shù)據(jù)如下

mysql> select *from student;
+----+--------+--------+
| id | name   | class  |
+----+--------+--------+
|  1 | 張三   | 一班   |
+----+--------+--------+
1 row in set (0.01 sec)

假設(shè)插入該記錄的事務(wù)id為8,那么此刻該條記錄的示意圖如下所示:
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC

insert undo只在事務(wù)回滾時(shí)起作用,當(dāng)事務(wù)提交后,該類型的undo日志就沒用了,它占用的UndoLog Segment也會(huì)被系統(tǒng)回收(也就是該undo日志占用的Undo頁面鏈表要么被重用,要么被釋放)。

假設(shè)之后兩個(gè)事務(wù)id分別為 10 、 20 的事務(wù)對(duì)這條記錄進(jìn)行UPDATE操作,操作流程如下:
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC

能不能在兩個(gè)事務(wù)中交叉更新同一條記錄呢?不能!這不就是一個(gè)事務(wù)修改了另一個(gè)未提交事務(wù)修改過的數(shù)據(jù),臟寫。
InnoDB使用鎖來保證不會(huì)有臟寫情況的發(fā)生,也就是在第一個(gè)事務(wù)更新了某條記錄后,就會(huì)給這條記錄加鎖,另一個(gè)事務(wù)再次更新時(shí)就需要等待第一個(gè)事務(wù)提交了,把鎖釋放之后才可以繼續(xù)更新

每次對(duì)記錄進(jìn)行改動(dòng),都會(huì)記錄一條undo日志,每條undo日志也都有一個(gè)roll_pointer屬性(INSERT操作對(duì)應(yīng)的undo日志沒有該屬性,因?yàn)樵撚涗洸]有更早的版本),可以將這些undo日志都連起來,串成一個(gè)鏈表:
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC
對(duì)該記錄每次更新后,都會(huì)將舊值放到一條undo日志中,就算是該記錄的一個(gè)舊版本,隨著更新次數(shù)的增多,所有的版本都會(huì)被roll_pointer屬性連接成一個(gè)鏈表,我們把這個(gè)鏈表稱之為版本鏈,版本鏈的頭節(jié)點(diǎn)就是當(dāng)前記錄最新的值。
每個(gè)版本中還包含生成該版本時(shí)對(duì)應(yīng)的事務(wù)id。

MVCC實(shí)現(xiàn)原理之ReadView

:::success
MVCC 的實(shí)現(xiàn)依賴于: 隱藏字段、Undo Log、Read View
:::

什么是ReadView

在MVCC機(jī)制中,多個(gè)事務(wù)對(duì)同一個(gè)行記錄進(jìn)行更新會(huì)產(chǎn)生多個(gè)歷史快照,這些歷史快照保存在Undo Log里。如果一個(gè)事務(wù)想要查詢這個(gè)行記錄,需要讀取哪個(gè)版本的行記錄呢?這時(shí)就需要用到ReadView了,它幫我們解決了行的可見性問題。
ReadView就是事務(wù)在使用MVCC機(jī)制進(jìn)行快照讀操作時(shí)產(chǎn)生的讀視圖。當(dāng)事務(wù)啟動(dòng)時(shí),會(huì)生成數(shù)據(jù)庫系統(tǒng)當(dāng)前的一個(gè)快照,InnoDB為每個(gè)事務(wù)構(gòu)造了一個(gè)數(shù)組,用來記錄并維護(hù)系統(tǒng)當(dāng)前活躍事務(wù)的ID(“活躍"指的就是,啟動(dòng)了但還沒提交)。

設(shè)計(jì)思路

使用READ UNCOMMITTED隔離級(jí)別的事務(wù),由于可以讀到未提交事務(wù)修改過的記錄,所以讀到的是最新版本的記錄。
使用SERIALIZABLE隔離級(jí)別的事務(wù),InnoDB規(guī)定使用加鎖的方式來訪問記錄,因此讀取到的記錄也是最新版本。
使用READ COMMITTEDREPEATABLE READ隔離級(jí)別的事務(wù),都必須保證讀到已經(jīng)提交了的事務(wù)修改過的記錄。假如另一個(gè)事務(wù)已經(jīng)修改了記錄但是尚未提交,是不能直接讀取最新版本的記錄的,核心問題就是需要判斷一下版本鏈中的哪個(gè)版本是當(dāng)前事務(wù)可見的,這是ReadView要解決的主要問題。
ReadView中主要包含 4 個(gè)比較重要的內(nèi)容,分別如下:

  1. creator_trx_id,創(chuàng)建這個(gè) Read View 的事務(wù) ID。

說明:只有在對(duì)表中的記錄做改動(dòng)時(shí)(執(zhí)行INSERT、DELETE、UPDATE這些語句時(shí))才會(huì)為事務(wù)分配事務(wù)id,否則在一個(gè)只讀事務(wù)中的事務(wù)id值都默認(rèn)為 0 。

  1. trx_ids,表示在生成ReadView時(shí)當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)的事務(wù)id列表
  2. up_limit_id,活躍的事務(wù)中最小的事務(wù) ID。
  3. low_limit_id,表示生成ReadView時(shí)系統(tǒng)中應(yīng)該分配給下一個(gè)事務(wù)的id值。low_limit_id 是系統(tǒng)最大的事務(wù)id值,這里要注意是系統(tǒng)中的事務(wù)id,需要區(qū)別于正在活躍的事務(wù)ID。

注意:low_limit_id并不是trx_ids中的最大值,事務(wù)id是遞增分配的。比如,現(xiàn)在有id為 1 ,2 , 3 這三個(gè)事務(wù),之后id為 3 的事務(wù)提交了。那么一個(gè)新的讀事務(wù)在生成ReadView時(shí),trx_ids就包括 1 和 2 ,up_limit_id的值就是 1 ,low_limit_id的值就是 4 。

**舉例:
**trx_ids為tr2、tr3、tr:5和trx8的集合,系統(tǒng)的最大事務(wù)ID (low_limit_id)為trx8+1(如果之前沒有其他的新增事務(wù)),活躍的最小事務(wù)ID (up_limit_id)為trx2。
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC

ReadView的規(guī)則

有了這個(gè)ReadView,這樣在訪問某條記錄時(shí),只需要按照下邊的步驟判斷記錄的某個(gè)版本是否可見。

  • 如果被訪問版本的trx_id屬性值與ReadView中的creator_trx_id值相同,意味著當(dāng)前事務(wù)在訪問它自己修改過的記錄,所以該版本可以被當(dāng)前事務(wù)訪問。
  • 如果被訪問版本的trx_id屬性值小于ReadView中的up_limit_id值,表明生成該版本的事務(wù)在當(dāng)前事務(wù)生成ReadView前已經(jīng)提交,所以該版本可以被當(dāng)前事務(wù)訪問。
  • 如果被訪問版本的trx_id屬性值大于或等于ReadView中的low_limit_id值,表明生成該版本的事務(wù)在當(dāng)前事務(wù)生成ReadView后才開啟,所以該版本不可以被當(dāng)前事務(wù)訪問。
  • 如果被訪問版本的trx_id屬性值在ReadView的up_limit_id和low_limit_id之間,那就需要判斷一下trx_id屬性值是不是在trx_ids列表中。
    • 如果在,說明創(chuàng)建ReadView時(shí)生成該版本的事務(wù)還是活躍的,該版本不可以被訪問。
    • 如果不在,說明創(chuàng)建ReadView時(shí)生成該版本的事務(wù)已經(jīng)被提交,該版本可以被訪問。

MVCC整體操作流程

了解了這些概念之后,我們來看下當(dāng)查詢一條記錄的時(shí)候,系統(tǒng)如何通過MVCC找到它:

  1. 首先獲取事務(wù)自己的版本號(hào),也就是事務(wù) ID;
  2. 生成 ReadView;
  3. 查詢得到的數(shù)據(jù),然后與 ReadView 中的事務(wù)版本號(hào)進(jìn)行比較;
  4. 如果不符合 ReadView 規(guī)則,就需要從 Undo Log 中獲取歷史快照;
  5. 最后返回符合規(guī)則的數(shù)據(jù)。

如果某個(gè)版本的數(shù)據(jù)對(duì)當(dāng)前事務(wù)不可見的話,那就順著版本鏈找到下一個(gè)版本的數(shù)據(jù),繼續(xù)按照上邊的步驟判斷可見性,依此類推,直到版本鏈中的最后一個(gè)版本。如果最后一個(gè)版本也不可見的話,那么就意味著該條記錄對(duì)該事務(wù)完全不可見,查詢結(jié)果就不包含該記錄。

lnnoDB中,MVCC是通過Undo Log + Read View進(jìn)行數(shù)據(jù)讀取,Undo Log保存了歷史快照,而Read View規(guī)則幫我們判斷當(dāng)前版本的數(shù)據(jù)是否可見。

在隔離級(jí)別為讀已提交(Read Committed)時(shí),一個(gè)事務(wù)中的每一次 SELECT 查詢都會(huì)重新獲取一次Read View。
如表所示:
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC

注意,此時(shí)同樣的查詢語句都會(huì)重新獲取一次 Read View,這時(shí)如果 Read View 不同,就可能產(chǎn)生不可重復(fù)讀或者幻讀的情況。

當(dāng)隔離級(jí)別為可重復(fù)讀的時(shí)候,就避免了不可重復(fù)讀,這是因?yàn)橐粋€(gè)事務(wù)只在第一次 SELECT 的時(shí)候會(huì)獲取一次 Read View,而后面所有的 SELECT 都會(huì)復(fù)用這個(gè) Read View,如下表所示:
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC

舉例說明整個(gè)流程

假設(shè)現(xiàn)在student表中只有一條由事務(wù)id為8的事務(wù)插入的一條記錄:

mysql> select *from student;
+----+--------+--------+
| id | name   | class  |
+----+--------+--------+
|  1 | 張三   | 一班   |
+----+--------+--------+
1 row in set (0.01 sec)

MVCC只能在READ COMMITTEDREPEATABLE READ兩個(gè)隔離級(jí)別下工作。接下來看一下READ COMMITTEDREPEATABLE READ所謂的生成ReadView的時(shí)機(jī)不同到底不同在哪里。

Read Commited隔離級(jí)別下

READ COMMITTED :每次讀取數(shù)據(jù)前都生成一個(gè)ReadView 。
現(xiàn)在有兩個(gè)事務(wù)id分別為 10 、 20 的事務(wù)在執(zhí)行:

# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id= 1 ;
UPDATE student SET name="王五" WHERE id= 1 ;

# Transaction 20
BEGIN;
# 更新了一些別的表的記錄
...

說明:事務(wù)執(zhí)行過程中,只有在第一次真正修改記錄時(shí)(比如使用INSERT、DELETE、UPDATE語句),才會(huì)被 分配一個(gè)單獨(dú)的事務(wù)id,這個(gè)事務(wù)id是遞增的。所以我們才在事務(wù)20中更新些別的表的記錄,目的是讓它分配事務(wù)id。

此刻,表student中id為1的記錄得到的版本鏈表如下所示:
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC
假設(shè)現(xiàn)在有一個(gè)使用READ COMMITTED隔離級(jí)別的事務(wù)開始執(zhí)行:

# 使用READ COMMITTED隔離級(jí)別的事務(wù)
BEGIN;

# SELECT1:Transaction 10、 20 未提交
SELECT * FROM student WHERE id = 1 ; # 得到的列name的值為'張三'

這個(gè)SELECT1的執(zhí)行過程如下:
步驟1: 在執(zhí)行SELECT語句時(shí)會(huì)先生成一個(gè)ReadView , ReadView的 trx_ids列表的內(nèi)容就是[10,20],up_limit_id為10, low_limit_id為21, creator_trx_id為0。
步驟2:從版本鏈中挑選可見的記錄,從圖中看出,最新版本的列name的內(nèi)容是’王五’,該版本的trx_id值為10,在trx_ids列表內(nèi),所以不符合可見性要求,根據(jù)roll_pointer跳到下一個(gè)版本。
步驟3:下一個(gè)版本的列name的內(nèi)容是’李四’,該版本的trx_id值也為10,也在trx_ids列表內(nèi),所以也不符合要求,繼續(xù)跳到下一個(gè)版本。
步驟4:下一個(gè)版本的列name的內(nèi)容是’張三’,該版本的trx_id值為8,小于ReadView中的up_limit_id值10,所以這個(gè)版本是符合要求的,最后返回給用戶的版本就是這條列name為‘張三’的記錄。
之后,我們把事務(wù)id為 10 的事務(wù)提交一下:

# Transaction 10
BEGIN;

UPDATE student SET name="李四" WHERE id= 1 ;
UPDATE student SET name="王五" WHERE id= 1 ;

COMMIT;

然后再到事務(wù)id為 20 的事務(wù)中更新一下表student中id為 1 的記錄:

# Transaction 20
BEGIN;

# 更新了一些別的表的記錄
...
UPDATE student SET name="錢七" WHERE id= 1 ;
UPDATE student SET name="宋八" WHERE id= 1 ;

此刻,表student中id為 1 的記錄的版本鏈就長(zhǎng)這樣:
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC
然后再到剛才使用READ COMMITTED隔離級(jí)別的事務(wù)中繼續(xù)查找這個(gè)id為 1 的記錄,如下:

# 使用READ COMMITTED隔離級(jí)別的事務(wù)
BEGIN;

# SELECT1:Transaction 10、 20 均未提交
SELECT * FROM student WHERE id = 1 ; # 得到的列name的值為'張三'

# SELECT2:Transaction 10提交,Transaction 20未提交
SELECT * FROM student WHERE id = 1 ; # 得到的列name的值為'王五'

這個(gè)SELECT2的執(zhí)行過程如下:
步驟1:在執(zhí)行SELECT語句時(shí)會(huì)又會(huì)單獨(dú)生成一個(gè)ReadView,該ReadView的trx_ids列表的內(nèi)容就是[20],up_limit_id為.20,low_limit_id為21, creator_trx_id為0。
步驟2:從版本鏈中挑選可見的記錄,從圖中看出,最新版本的列name的內(nèi)容是‘宋八‘,該版本的trx_id值為20,在trx_ids列表內(nèi),所以不符合可見性要求,根據(jù)roll_pointer跳到下一個(gè)版本。
步驟3:下一個(gè)版本的列name的內(nèi)容是‘錢七’,該版本的trx_id值為20,也在trx_ids列表內(nèi),所以也不符合要求,繼續(xù)跳到下一個(gè)版本。
步驟4:下一個(gè)版本的列name的內(nèi)容是’王五’,該版本的trx_id值為10,小于ReadView中的up_limit_id值20,所以這個(gè)版本是符合要求的,最后返回給用戶的版本就是這條列name為‘王五‘的記錄。

強(qiáng)調(diào): 使用READ COMMITTED隔離級(jí)別的事務(wù)在每次查詢開始時(shí)都會(huì)生成一個(gè)獨(dú)立的ReadView。

Repeatable Read隔離級(jí)別下

Repeatable Read:只會(huì)生成一個(gè)ReadView
比如,系統(tǒng)里有兩個(gè)事務(wù)id分別為 10 、 20 的事務(wù)在執(zhí)行:

# 開始記錄
mysql> select *from student;
+----+--------+--------+
| id | name   | class  |
+----+--------+--------+
|  1 | 張三   | 一班   |
+----+--------+--------+
1 row in set (0.01 sec)
# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id= 1 ;
UPDATE student SET name="王五" WHERE id= 1 ;

# Transaction 20
BEGIN;
# 更新了一些別的表的記錄
...

此刻,表student 中id為 1 的記錄得到的版本鏈表如下所示:
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC
假設(shè)現(xiàn)在有一個(gè)使用REPEATABLE READ隔離級(jí)別的事務(wù)開始執(zhí)行:

# 使用REPEATABLE READ隔離級(jí)別的事務(wù)
BEGIN;

# SELECT1:Transaction 10、 20 未提交
SELECT * FROM student WHERE id = 1 ; # 得到的列name的值為'張三'

這個(gè)SELECT1的執(zhí)行過程如下(第一個(gè)ReadView和讀已提交是一樣的):
步驟1: 在執(zhí)行SELECT語句時(shí)會(huì)先生成一個(gè)ReadView , ReadView的 trx_ids列表的內(nèi)容就是[10,20],up_limit_id為10, low_limit_id為21, creator_trx_id為0。
步驟2:從版本鏈中挑選可見的記錄,從圖中看出,最新版本的列name的內(nèi)容是’王五’,該版本的trx_id值為10,在trx_ids列表內(nèi),所以不符合可見性要求,根據(jù)roll_pointer跳到下一個(gè)版本。
步驟3:下一個(gè)版本的列name的內(nèi)容是’李四’,該版本的trx_id值也為10,也在trx_ids列表內(nèi),所以也不符合要求,繼續(xù)跳到下一個(gè)版本。
步驟4:下一個(gè)版本的列name的內(nèi)容是’張三’,該版本的trx_id值為8,小于ReadView中的up_limit_id值10,所以這個(gè)版本是符合要求的,最后返回給用戶的版本就是這條列name為‘張三’的記錄。
之后,我們把事務(wù)id為 10 的事務(wù)提交一下:

# Transaction 10
BEGIN;

UPDATE student SET name="李四" WHERE id= 1 ;
UPDATE student SET name="王五" WHERE id= 1 ;

COMMIT;

然后再到事務(wù)id為 20 的事務(wù)中更新一下表student中id為 1 的記錄:

# Transaction 20
BEGIN;

# 更新了一些別的表的記錄
...
UPDATE student SET name="錢七" WHERE id= 1 ;
UPDATE student SET name="宋八" WHERE id= 1 ;

此刻,表student 中id為 1 的記錄的版本鏈長(zhǎng)這樣:
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC
然后再到剛才使用REPEATABLE READ隔離級(jí)別的事務(wù)中繼續(xù)查找這個(gè)id為 1 的記錄,如下:

# 使用REPEATABLE READ隔離級(jí)別的事務(wù)
BEGIN;

# SELECT1:Transaction 10、 20 均未提交
SELECT * FROM student WHERE id = 1 ; # 得到的列name的值為'張三'

# SELECT2:Transaction 10提交,Transaction 20未提交
SELECT * FROM student WHERE id = 1 ; # 得到的列name的值仍為'張三'

這個(gè)SELECT2的執(zhí)行過程如下:
步驟1:在執(zhí)行SELECT語句時(shí)會(huì)繼續(xù)使用之前的ReadView,該ReadView的trx_ids列表的內(nèi)容就是[10,20],up_limit_id為10, low_limit_id為21, creator_trx_id為0。
步驟2:然后從版本鏈中挑選可見的記錄,從圖中可以看出,最新版本的列name的內(nèi)容是’宋八’,該版本的trx_id值為20,在trx_ids列表內(nèi),所以不符合可見性要求,根據(jù)roll_pointer跳到下一個(gè)版本。
步驟3:下一個(gè)版本的列name的內(nèi)容是’錢七’,該版本的trx_id值為20,也在trx_ids列表內(nèi),所以也不符合要求,繼續(xù)跳到下一個(gè)版本。
步驟4∶下一個(gè)版本的列name的內(nèi)容是’王五’,該版本的trx_id值為10,而trx_ids列表中是包含值為10的事務(wù)id的,所以該版本也不符合要求,同理下一個(gè)列name的內(nèi)容是’李四’的版本也不符合要求。繼續(xù)跳到下一個(gè)版本。
步驟5∶下一個(gè)版本的列name的內(nèi)容是‘張三’,該版本的trx_id值為80,小于ReadView中的up_limit_id值10,所以這個(gè)版本是符合要求的,最后返回給用戶的版本就是這條列c為‘張三’的記錄。
兩次SELECT查詢得到的結(jié)果是重復(fù)的,記錄的列c值都是‘張三’,這就是可重復(fù)讀的含義。如果我們之后再把事務(wù)id為20的記錄提交了,然后再到剛才使用REPEATABLE READ隔離級(jí)別的事務(wù)中繼續(xù)查找這個(gè)id為1的記錄,得到的結(jié)果還是‘張三’,具體執(zhí)行過程大家可以自己分析一下。

解決幻讀問題

假設(shè)現(xiàn)在表 student 中只有一條數(shù)據(jù),數(shù)據(jù)內(nèi)容中,主鍵 id=1,隱藏的 trx_id=10,它的 undo log 如下圖所示。
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC
假設(shè)現(xiàn)在有事務(wù) A 和事務(wù) B 并發(fā)執(zhí)行,事務(wù) A 的事務(wù) id 為 20 ,事務(wù) B 的事務(wù) id 為 30 。
步驟 1 :事務(wù) A 開始第一次查詢數(shù)據(jù),查詢的 SQL 語句如下。

select * from student where id >= 1 ;

在開始查詢之前,MySQL 會(huì)為事務(wù) A 產(chǎn)生一個(gè) ReadView,此時(shí) ReadView 的內(nèi)容如下:trx_ids=[20,30],up_limit_id=20,low_limit_id=31,creator_trx_id=20。
由于此時(shí)表 student 中只有一條數(shù)據(jù),且符合 where id>=1 條件,因此會(huì)查詢出來。然后根據(jù) ReadView 機(jī)制,發(fā)現(xiàn)該行數(shù)據(jù)的trx_id=10,小于事務(wù) A 的 ReadView 里 up_limit_id,這表示這條數(shù)據(jù)是事務(wù) A 開 啟之前,其他事務(wù)就已經(jīng)提交了的數(shù)據(jù),因此事務(wù) A 可以讀取到。
結(jié)論:事務(wù) A 的第一次查詢,能讀取到一條數(shù)據(jù),id=1。
步驟 2 :接著事務(wù) B(trx_id=30),往表 student 中新插入兩條數(shù)據(jù),并提交事務(wù)。

insert into student(id,name) values( 2 ,'李四');
insert into student(id,name) values( 3 ,'王五');

此時(shí)表student 中就有三條數(shù)據(jù)了,對(duì)應(yīng)的 undo 如下圖所示:
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC
步驟 3 :接著事務(wù) A 開啟第二次查詢,根據(jù)可重復(fù)讀隔離級(jí)別的規(guī)則,此時(shí)事務(wù) A 并不會(huì)再重新生成ReadView。此時(shí)表 student 中的 3 條數(shù)據(jù)都滿足 where id>=1 的條件,因此會(huì)先查出來。然后根據(jù)ReadView 機(jī)制,判斷每條數(shù)據(jù)是不是都可以被事務(wù) A 查到。
1 )首先 id=1 的這條數(shù)據(jù),前面已經(jīng)說過了,可以被事務(wù) A 看到。
2 )然后是 id=2 的數(shù)據(jù),它的 trx_id=30,此時(shí)事務(wù) A 發(fā)現(xiàn),這個(gè)值處于 up_limit_id 和 low_limit_id 之 間,因此還需要再判斷 30 是否處于 trx_ids 數(shù)組內(nèi)。由于事務(wù) A 的 trx_ids=[20,30],因此在數(shù)組內(nèi),意味著id=2 的這條數(shù)據(jù)是與事務(wù) A 在同一時(shí)刻啟動(dòng)的其他事務(wù)提交的,所以這條數(shù)據(jù)不能讓事務(wù) A 看到。
3 )同理,id=3 的這條數(shù)據(jù),trx_id 也為 30 ,因此也不能被事務(wù) A 看見。
多版本并發(fā)控制MVCC,MySql,mysql,數(shù)據(jù)庫,MVCC
結(jié)論:最終事務(wù) A 的第二次查詢,只能查詢出 id=1 的這條數(shù)據(jù)。這和事務(wù) A 的第一次查詢的結(jié)果是一樣的,因此沒有出現(xiàn)幻讀現(xiàn)象,所以說在 MySQL 的可重復(fù)讀隔離級(jí)別下,不存在幻讀問題。文章來源地址http://www.zghlxwxcb.cn/news/detail-721440.html

到了這里,關(guān)于多版本并發(fā)控制MVCC的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【MySQL高級(jí)篇筆記-多版本并發(fā)控制MVCC(下) 】

    【MySQL高級(jí)篇筆記-多版本并發(fā)控制MVCC(下) 】

    此筆記為尚硅谷MySQL高級(jí)篇部分內(nèi)容 目錄 一、什么是MVCC 二、快照讀與當(dāng)前讀 1、快照讀? 2、當(dāng)前讀 三、復(fù)習(xí) 1、再談隔離級(jí)別 2、隱藏字段、Undo Log版本鏈 四、MVCC實(shí)現(xiàn)原理之ReadView? 1、什么是ReadView? 2、設(shè)計(jì)思路 3、ReadView的規(guī)則 4、MVCC整體操作流程 五、舉例說明 1、READ

    2024年02月08日
    瀏覽(23)
  • ⑩⑧【MySQL】InnoDB架構(gòu)、事務(wù)原理、MVCC多版本并發(fā)控制

    ⑩⑧【MySQL】InnoDB架構(gòu)、事務(wù)原理、MVCC多版本并發(fā)控制

    個(gè)人簡(jiǎn)介:Java領(lǐng)域新星創(chuàng)作者;阿里云技術(shù)博主、星級(jí)博主、專家博主;正在Java學(xué)習(xí)的路上摸爬滾打,記錄學(xué)習(xí)的過程~ 個(gè)人主頁:.29.的博客 學(xué)習(xí)社區(qū):進(jìn)去逛一逛~ InnoDB邏輯存儲(chǔ)結(jié)構(gòu) : ?? 表空間(idb文件) :一個(gè)MySQL實(shí)例可以對(duì)應(yīng)多個(gè)表空間,用于存儲(chǔ)記錄、索引等數(shù)

    2024年02月04日
    瀏覽(53)
  • 深入探索MySQL InnoDB引擎中的多版本并發(fā)控制(MVCC)原理

    引言: 在關(guān)系型數(shù)據(jù)庫領(lǐng)域,MySQL InnoDB引擎因其支持事務(wù)處理和并發(fā)控制的強(qiáng)大功能而備受青睞,其中尤為關(guān)鍵的一項(xiàng)技術(shù)就是多版本并發(fā)控制(Multi-Version Concurrency Control, MVCC)。MVCC是實(shí)現(xiàn)高并發(fā)環(huán)境下事務(wù)隔離性的重要手段,既能有效緩解鎖競(jìng)爭(zhēng),又能保障數(shù)據(jù)的一致性

    2024年03月11日
    瀏覽(18)
  • MYSQL的多版本并發(fā)控制MVCC(Multi-Version Concurrency Control)

    MYSQL的多版本并發(fā)控制MVCC(Multi-Version Concurrency Control)

    MVCC 是一種用于數(shù)據(jù)庫管理系統(tǒng)的并發(fā)控制技術(shù),允許多個(gè)事務(wù)同時(shí)訪問數(shù)據(jù)庫,而不會(huì)導(dǎo)致讀寫沖突。也就是說在讀寫的時(shí)候,線程不用去爭(zhēng)搶讀寫鎖。因?yàn)榧渔i的過程比較耗性能。 當(dāng)然很多時(shí)候還是必須的,不能避免,比如說,去ATM機(jī)取錢的時(shí)候,同時(shí)又在手機(jī)APP上進(jìn)行

    2024年02月07日
    瀏覽(27)
  • Mysql--技術(shù)文檔--MVCC(Multi-Version Concurrency Control | 多版本并發(fā)控制)

    Mysql--技術(shù)文檔--MVCC(Multi-Version Concurrency Control | 多版本并發(fā)控制)

    ????????MVCC(Multi-Version Concurrency Control)是一種并發(fā)控制機(jī)制,用于解決并發(fā)訪問數(shù)據(jù)庫時(shí)的數(shù)據(jù)一致性和隔離性問題。MVCC允許多個(gè)事務(wù)同時(shí)讀取數(shù)據(jù)庫的同一數(shù)據(jù),而不會(huì)相互干擾或?qū)е聸_突。 ????????在傳統(tǒng)的并發(fā)控制機(jī)制中,如鎖定機(jī)制,事務(wù)會(huì)對(duì)讀取和寫入

    2024年02月11日
    瀏覽(20)
  • mysql數(shù)據(jù)庫 mvcc

    mysql數(shù)據(jù)庫 mvcc

    ? 在看MVCC之前我們先補(bǔ)充些基礎(chǔ)內(nèi)容,首先來看下事務(wù)的ACID和數(shù)據(jù)的總體運(yùn)行流程 ? ?數(shù)據(jù)庫整體的使用流程: ACID流程圖? mysql核心日志: 在MySQL數(shù)據(jù)庫中有三個(gè)非常重要的日志binlog,undolog,redolog. mvcc概念介紹: MVCC (Multi-Version Concurrency Control): 多版本并發(fā)控制,是一種并發(fā)

    2024年02月20日
    瀏覽(27)
  • MySQL高級(jí)特性篇(7)-數(shù)據(jù)庫版本控制與遷移

    MySQL數(shù)據(jù)庫版本控制與遷移 在軟件開發(fā)的過程中,數(shù)據(jù)庫版本控制和遷移是非常重要的一部分。這些過程確保了數(shù)據(jù)庫的結(jié)構(gòu)及數(shù)據(jù)的追蹤和更新。在本篇博客中,我們將介紹如何使用Markdown語法來編寫MySQL數(shù)據(jù)庫版本控制與遷移的相關(guān)內(nèi)容。 MySQL數(shù)據(jù)庫版本控制與遷移是指在

    2024年02月22日
    瀏覽(24)
  • MVCC------Mysql并發(fā)事務(wù)控制的工具

    MVCC------Mysql并發(fā)事務(wù)控制的工具

    MVCC 是 Multi-Version Concurrency Control(多版本并發(fā)控制)的縮寫,是數(shù)據(jù)庫系統(tǒng)中常用的一種并發(fā)控制方法。在MVCC 中,數(shù)據(jù)庫系統(tǒng)可以同時(shí)維護(hù)多個(gè)版本的數(shù)據(jù),每個(gè)事務(wù)在讀取數(shù)據(jù)時(shí)會(huì)看到一個(gè)一致性的快照,從而實(shí)現(xiàn)并發(fā)訪問而不會(huì)出現(xiàn)數(shù)據(jù)不一致的情況。這種機(jī)制能夠提

    2024年03月14日
    瀏覽(32)
  • 【Mysql】MVCC版本機(jī)制的多并發(fā)

    【Mysql】MVCC版本機(jī)制的多并發(fā)

    ??個(gè)人主頁:平凡的小蘇 ??學(xué)習(xí)格言:命運(yùn)給你一個(gè)低的起點(diǎn),是想看你精彩的翻盤,而不是讓你自甘墮落,腳下的路雖然難走,但我還能走,比起向陽而生,我更想嘗試逆風(fēng)翻盤 。 ?? Mysql專欄 : Mysql內(nèi)功修煉基地 家人們更新不易,你們的??點(diǎn)贊??和?關(guān)注?真的對(duì)我

    2024年02月11日
    瀏覽(24)
  • 多版本并發(fā)控制MVCC

    多版本并發(fā)控制MVCC

    MVCC (Multiversion Concurrency Control),多版本并發(fā)控制。顧名思義,MVCC 是通過數(shù)據(jù)行的多個(gè)版本管理來實(shí)現(xiàn)數(shù)據(jù)庫的 并發(fā)控制 。這項(xiàng)技術(shù)使得在InnoDB的事務(wù)隔離級(jí)別下執(zhí)行一致性讀操作有了保證。換言之,就是為了查詢一些正在被另一個(gè)事務(wù)更新的行,并且可以看到它們被更

    2024年02月08日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包