MySQL 8.0:InnoDB中大對(duì)象的MVCC
在本文中,我將解釋MySQL InnoDB存儲(chǔ)引擎中大對(duì)象(LOB)設(shè)計(jì)的多版本并發(fā)控制(MVCC) 。 MySQL 8.0有一個(gè)新功能,允許用戶部分更新大型對(duì)象,包括JSON文檔 。 使用此部分更新功能,當(dāng)LOB部分更新時(shí),MVCC對(duì)LOB的工作方式已發(fā)生變化。 對(duì)于正常更新(完整更新),MVCC將像以前的版本一樣工作。 讓我們看一下MVCC在不涉及部分更新時(shí)的工作原理,然后考慮對(duì)LOB進(jìn)行部分更新的用例。
MVCC 常規(guī)更新
我使用術(shù)語常規(guī)更新來指代不是部分更新的更新。 我將通過一個(gè)例子解釋MVCC如何用于常規(guī)更新大對(duì)象。 我將為此目的使用以下mtr(1)測(cè)試用例:
create table t1 ( f1 int primary key , f2 longblob ) engine = innodb ;
insert into t1 values ( 1 , repeat ( 'a' , 65536 ) ) ;
start transaction ;
update t1 set f2 = repeat ( 'b' , 65536 ) where f1 = 1 ;
-- echo # Connection con1:
-- 對(duì)于使用MySQL客戶端的用戶,可能需要通過另開一個(gè)終端窗口建立新鏈接, 下同。
connect ( con1 , localhost , root , , ) ;
-- echo # Must see the old value 'aaaaaaaaaa'
select f1 , right ( f2 , 10 ) from t1 order by f1 ;
-- echo # Connection default:
connection default ;
disconnect con1 ;
commit ;
drop table t1 ;
為了理解下面的解釋,仔細(xì)理解上述測(cè)試用例非常重要。
測(cè)試場(chǎng)景如下:
最初,表t1包含單個(gè)記錄(R1)。
事務(wù)trx1將記錄更新為新值。
當(dāng)trx1仍處于活動(dòng)狀態(tài)時(shí),另一個(gè)事務(wù)trx2正在讀取記錄。 它將讀取舊值。
表t1僅包含一個(gè)記錄(R1)。 但是trx1和trx2會(huì)看到兩個(gè)不同的值。 該表實(shí)際上只包含最新值(trx1所見的值),而trx2看到的值或記錄是從撤消日志記錄中獲得的。 讓我們看下面的圖片來更好地理解它。
初始狀態(tài):更新操作之前
下圖顯示了更新操作之前的情況。 撤消日志為空。 表的聚簇索引包含一行。 表中有一個(gè)LOB。 聚簇索引記錄包含對(duì)LOB的引用。
![](http://img.jbzj.com/file_images/article/201811/2018112162102153.png?2018102162112)
最終狀態(tài):更新操作后
現(xiàn)在讓我們看一下更新操作后的情況。
![](http://img.jbzj.com/file_images/article/201811/2018112162130588.png?2018102162137)
以下是一些重要的觀察:
用戶表空間中有兩個(gè)LOB - 舊的LOB和新的LOB。 舊的LOB只能通過撤消日志訪問。 聚集索引記錄指向新LOB。
更新操作已創(chuàng)建包含更新向量的撤消日志記錄。 此撤消日志記錄指向舊LOB。
聚簇索引記錄通過DB_ROLL_PTR系統(tǒng)列指向撤消日志記錄。 此滾動(dòng)指針指向撤消日志記錄,該記錄可用于構(gòu)建聚簇索引記錄的先前版本。
撤消記錄不包含LOB本身。 而是它只包含對(duì)存儲(chǔ)在用戶表空間中的LOB的引用。
存儲(chǔ)在撤消日志記錄中的LOB引用與存儲(chǔ)在聚簇索引記錄中的LOB引用不同。
事務(wù)在連接1中采取的步驟如下:
事務(wù)查看R1并確定尚未提交修改聚簇索引記錄的事務(wù)。 這意味著它無法讀取該記錄(因?yàn)槟J(rèn)隔離級(jí)別是REPEATABLE READ)。
它查看R1中的DB_ROLL_PTR并找到撤消日志記錄。 使用撤消日志記錄構(gòu)建R1的先前版本。
它讀取了這個(gè)構(gòu)建的舊版R1。 請(qǐng)注意,此版本在聚簇索引記錄中不可用。 但它使用撤消記錄即時(shí)構(gòu)建。
當(dāng)R1指向新的LOB時(shí),這個(gè)構(gòu)造的舊版本的R1指向舊的LOB。 所以結(jié)果包含舊的LOB。
這是LOB的MVCC在不涉及部分更新時(shí)的工作方式。
MVCC部分更新
讓我們看另一個(gè)例子,了解MVCC在部分更新的情況下是如何工作的。 我們需要另一個(gè)例子,因?yàn)槟壳皟H通過函數(shù)json_set()和json_replace()支持JSON文檔的部分更新。
create table t2 ( f1 int primary key , j json ) engine = InnoDB ;
set @ elem_a = concat ( '"' , repeat ( 'a' , 200 ) , '"' ) ;
set @ elem_a_with_coma = concat ( @ elem_a , ',' ) ;
set @ json_doc = concat ( "[" , repeat ( @ elem_a_with_coma , 300 ) , @ elem_a , "]" ) ;
insert into t2 ( f1 , j ) values ( 1 , @ json_doc ) ;
start transaction ;
update t2 set j = json_set ( j , '$[200]' , repeat ( 'b' , 200 ) ) where f1 = 1 ;
-- echo # Connection con1:
connect ( con1 , localhost , root , , ) ;
-- echo # Must see the old value 'aaaaaaaaaa...'
select json_extract ( j , '$[200]' ) from t2 ;
-- echo # Connection default:
connection default ;
disconnect con1 ;
commit ;
該場(chǎng)景與前面的示例相同。 只是longblob字段已更改為JSON文檔。 加載的數(shù)據(jù)也略有不同,以符合JSON格式。
提示 :您可以在上述mtr測(cè)試用例(兩者中)中添加語句set debug ='+ d,innodb_lob_print' ,以在服務(wù)器日志文件中打印LOB索引。 LOB索引將在插入后立即打印。 LOB索引將為您提供存儲(chǔ)的LOB對(duì)象的結(jié)構(gòu)。
在部分更新操作之前
完全或部分更新操作之前的初始條件是相同的,并且已經(jīng)在上面給出。 但是在下圖中,提供了一些附加信息。
![](http://img.jbzj.com/file_images/article/201811/2018112162239347.png?2018102162246)
讓我們看看圖中顯示的其他信息:
存儲(chǔ)在聚簇索引記錄中的LOB引用現(xiàn)在包含LOB版本號(hào)v1。 在初始插入操作期間,將其設(shè)置為1,并在每次部分更新時(shí)遞增。
每個(gè)LOB數(shù)據(jù)頁面在LOB索引中都有一個(gè)條目。 每個(gè)條目都包含LOB版本信息。 每當(dāng)修改一個(gè)LOB數(shù)據(jù)頁時(shí),它將被復(fù)制到具有新數(shù)據(jù)的新LOB數(shù)據(jù)頁中,并且將創(chuàng)建具有遞增的LOB版本號(hào)的新LOB索引條目。
附加信息是LOB版本號(hào)。 這在聚集索引記錄中的LOB引用中以及LOB索引的每個(gè)條目中都可用。
部分更新操作后
下圖說明了部分更新操作后的情況。
![](http://img.jbzj.com/file_images/article/201811/2018112162645197.png?2018102162652)
這里最重要的優(yōu)化是用戶表空間中仍然只有一個(gè)LOB。 僅更新需要修改的那些LOB數(shù)據(jù)頁。 部分更新操作后的這個(gè)單個(gè)LOB包含舊版本和新版本的LOB。 圖中LOB數(shù)據(jù)頁面上的v1和v2標(biāo)簽說明了這一點(diǎn)。
另一個(gè)重要的觀察是撤消日志和聚簇索引記錄中的LOB引用指向同一個(gè)LOB。 但LOB引用包含不同的版本號(hào)。 撤消日志記錄中的LOB引用包含v1(舊版本號(hào)),聚簇索引記錄中的LOB引用包含新版本號(hào)v2。
LOB版本號(hào)的目的
如上所示,具有不同版本號(hào)的不同LOB引用指向相同的LOB。 單個(gè)LOB包含來自不同版本的部分。 LOB版本號(hào)用于獲取各種LOB引用指向的正確版本。 在本節(jié)中,我們將了解如何完成此操作。
LOB索引包含組成LOB的LOB頁面列表。 它包含LOB數(shù)據(jù)頁的頁碼,每個(gè)LOB數(shù)據(jù)頁包含的數(shù)據(jù)量以及版本號(hào)。 此列表的每個(gè)節(jié)點(diǎn)稱為L(zhǎng)OB索引條目。 每個(gè)LOB索引條目都包含舊版本的列表。 讓我們看一個(gè)說明上述部分更新測(cè)試用例的結(jié)構(gòu)的圖。
![](http://img.jbzj.com/file_images/article/201811/2018112162717755.png?2018102162724)
最初,在完成部分更新之前,LOB索引總共包含4個(gè)條目。 四個(gè)條目的頁碼是5,6,7和8.沒有LOB索引條目具有舊版本。 所有四個(gè)條目的版本號(hào)均為1。
部分更新完成后,我們注意到頁碼9已替換頁碼7,頁碼7現(xiàn)在被視為頁碼9的舊版本。頁碼9的版本號(hào)為2,并且頁碼7的版本號(hào)為1。
部分更新完成后,當(dāng)通過版本號(hào)為1的LOB引用訪問LOB時(shí),將查看第5頁的第一個(gè)索引條目。 它的版本號(hào)為1.如果索引條目中的版本號(hào)小于或等于 LOB引用中的版本號(hào),則將讀取該條目。 因此,將讀取第5頁。 然后將查看頁碼為6的索引條目。 它的版本號(hào)為1,因此將被讀取。 然后將查看頁碼為9的索引條目。 它的版本號(hào)為2.但是lob引用的版本號(hào)為1.如果索引條目中的版本號(hào)大于LOB引用中的版本號(hào),則不會(huì)讀取該條目。 由于頁碼9的條目具有版本2,因此將查看其舊版本。 將檢查頁碼為7的索引條目。 它的版本號(hào)為1,因此將被讀取。 在此之后,將檢查頁碼為8的索引條目。 它的版本號(hào)為1,因此也將被讀取。 這是訪問舊版LOB的方式。
部分更新完成后,當(dāng)通過版本號(hào)為2的LOB引用訪問LOB時(shí),將查看第5頁的第一個(gè)索引條目。 它的版本號(hào)為1.如果索引條目中的版本號(hào)小于或等于LOB引用中的版本號(hào),則將讀取該條目。 因此它將按順序讀取頁碼5,6,9,8。 由于版本號(hào)始終= 2,因此無需使用舊版本訪問頁碼7。
需要記住的一點(diǎn)是LOB在InnoDB中不是獨(dú)立存在的。 它被視為聚簇索引記錄的擴(kuò)展。LOB對(duì)事務(wù)是否可見并不由LOB模塊處理。 LOB模塊只是處理聚簇索引記錄。 如果事務(wù)訪問LOB,則意味著它已經(jīng)在聚簇索引記錄中的DB_TRX_ID的幫助下確定它可以查看LOB(而不是LOB的特定版本)。 所以我們不擔(dān)心LOB模塊中的那個(gè)方面。 我們只專注于為給定的LOB版本號(hào)提供正確的內(nèi)容。
結(jié)論
在本文中,我們了解了如何在InnoDB中為大對(duì)象完成MVCC。 當(dāng)對(duì)LOB進(jìn)行部分更新時(shí),多個(gè)LOB引用可以指向同一個(gè)LOB。 但他們將擁有不同的版本號(hào)。 使用這些LOB版本號(hào),可以訪問正確的LOB內(nèi)容。
希望您發(fā)現(xiàn)此信息有用。
謝謝你使用MySQL!
注釋:
(1) Mtr即Mini-transaction的縮寫,字面意思小事物,相對(duì)邏輯事物而言,我們把它稱作物理事物。屬于Innodb存儲(chǔ)引擎的底層模塊。主要用于鎖和日志信息。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
您可能感興趣的文章:- 詳解MySQL多版本并發(fā)控制機(jī)制(MVCC)源碼
- mysql的MVCC多版本并發(fā)控制的實(shí)現(xiàn)
- mysql并發(fā)控制原理知識(shí)點(diǎn)
- mysql多版本并發(fā)控制MVCC的實(shí)現(xiàn)
- MySQL四種事務(wù)隔離級(jí)別詳解
- MySQL 四種事務(wù)隔離級(jí)別詳解及對(duì)比
- 深入解析MySQL的事務(wù)隔離及其對(duì)性能產(chǎn)生的影響
- MySQL中Innodb的事務(wù)隔離級(jí)別和鎖的關(guān)系的講解教程
- MySQL數(shù)據(jù)庫(kù)事務(wù)隔離級(jí)別介紹(Transaction Isolation Level)
- MySQL系列之十 MySQL事務(wù)隔離實(shí)現(xiàn)并發(fā)控制