自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試官問我 InnoDB 的物理存儲結構!

數(shù)據(jù)庫 MySQL
所謂物理存儲結構,指的是 MySQL 的數(shù)據(jù)是怎么存儲在物理介質(zhì)上的,由哪些磁盤文件組成。所謂邏輯存儲結構,指的是這些數(shù)據(jù)是如何有結構地組織起來的。

前段時間去面試,面試官突然問我:聊聊 InnoDB 的物理存儲結構吧!

樹義突然又眼圈一黑,啥都想不起來了!

雖說之前有大致了解過 MySQL,但對 InnoDB 的物理結構,卻真的沒咋了解過!那么,今天就來聊聊 InnoDB 的物理存儲結構吧!

相信很多人都知道邏輯結構和物理結構這兩個概念,但是都很好奇它們的區(qū)別是什么?

簡單地說:所謂物理存儲結構,指的是 MySQL 的數(shù)據(jù)是怎么存儲在物理介質(zhì)上的,由哪些磁盤文件組成。所謂邏輯存儲結構,指的是這些數(shù)據(jù)是如何有結構地組織起來的。

此文所描述的 InnoDB 物理存儲結構,依據(jù)的是 MySQL 8.0 版本。

從 MySQL 官方文檔中,我們可知 InnoDB 在物理層面上可劃分為如下 7 個模塊:

  • 系統(tǒng)表空間(System Tablespace)
  • 雙寫緩存文件(Doublewrite Buffer Files)
  • Undo 表空間(Undo Tablespaces)
  • Redo Log 文件(Redo Log)
  • 獨占表空間(File-Per-Table Tablespaces)
  • 通用表空間(General Tablespaces)
  • 臨時表空間(Temporary Tablespaces)

而在這 7 個模塊中,最為關鍵的是如下三個模塊:系統(tǒng)表空間(System Tablespace)、獨立表空間(File-Per-Table Tablespaces)、日志文件組(Redo Log)。

InnoDB 磁盤存儲結構 MySQL 8.0

系統(tǒng)表空間(System Tablespace)

在開始之前,我們需要理解表空間這個概念。其實表空間,就是存儲表的空間之意。它是 InnoDB 用于描述數(shù)據(jù)存儲的術語,大致是存儲表相關數(shù)據(jù)的地方之意。系統(tǒng)表空間,顧名思義就是 InnoDB 這個系統(tǒng)的默認表空間,也叫共享表空間,對應的是 MySQL 數(shù)據(jù)目錄下的 ibdata1 文件。如果我們將 innodb_file_per_table 設置為 off,那么我們使用 CREATE TABLE 語句創(chuàng)建的表數(shù)據(jù),都會存儲在系統(tǒng)表空間中。

而如果我們將 innodb_file_per_table 設置為 on,則每個表將獨立地產(chǎn)生一個表空間文件,以 ibd 結尾,數(shù)據(jù)、索引、表的內(nèi)部數(shù)據(jù)字典信息都將保存在這個單獨的表空間文件中。但是還有一些信息,例如 undo log 信息,還是會保存在系統(tǒng)表空間中。例如我們在 test 數(shù)據(jù)庫中創(chuàng)建了一個名為 user 的表,那么就會在 MySQL 的數(shù)據(jù)文件夾的 test 文件夾下,有一個名為 user.ibd 文件。

我們注意到系統(tǒng)表空間還有一個名為 change buffer 的東西,它到底是啥東西呢?

change buffer,其實在之前版本是插入緩沖(insert buffer),后續(xù)版本變成了 change buffer(參考:內(nèi)幕 2.4.1 章節(jié))。這一塊區(qū)域就是 Change Buffer。5.5 之前叫 Insert Buffer 插入緩沖,現(xiàn)在也能支持 delete 和 update,最后把 Change Buffer 記錄到數(shù)據(jù)頁的操作叫做 merge。

這里面有一句話很關鍵:除了主鍵聚合索引外,還產(chǎn)生了一個 name 列的輔助索引,對于該非聚集索引來說,葉子節(jié)點的插入不再有序,這時就需要離散訪問非聚集索引頁,插入性能變低。

這句話的意思是:聚集索引的插入是有序自增的,那么插入的時候直接跟在后面就可以了,不需要再做其他隨機訪問操作。而由于插入的非聚集索引列,并不是有序的,可能會插在中間,這時候就需要看看中間這個頁是否加載到內(nèi)存里。如果加載了,那還好,那就做 B+ 樹操作,調(diào)整就行。不在內(nèi)存里的話,那就麻煩了,需要讀取多次 page 到內(nèi)存。

為了提高效率,就弄了一個 insert buffer,簡單地說,就是把多次 insert 或者修改操作,先緩存起來,然后等到差不多的時候,再一起去做,提高效率,避免多次 IO 讀取。

插入緩沖,并不是緩存的一部分,而是物理頁,對于非聚集索引的插入或更新操作,不是每一次直接插入索引頁。而是先判斷插入的非聚集索引頁是否在緩沖池中。如果在,則直接插入,如果不再,則先放入一個插入緩沖區(qū)中。然后再以一定的頻率執(zhí)行插入緩沖和非聚集索引頁子節(jié)點的合并操作。

當需要更新一個數(shù)據(jù)頁時,如果數(shù)據(jù)頁在內(nèi)存中就直接更新,而如果這個數(shù)據(jù)頁還沒有在內(nèi)存中的話,在不影響數(shù)據(jù)一致性的前提下,InnoDB 會將這些更新操作緩存在 change buffer 中,這樣就不需要從磁盤中讀入這個數(shù)據(jù)頁了。在下次查詢需要訪問這個數(shù)據(jù)頁的時候,將數(shù)據(jù)頁讀入內(nèi)存,然后執(zhí)行 change buffer 中與這個頁有關的操作。

雙寫緩存文件(Doublewrite Buffer Files)

如果說插入緩沖帶給 InnoDB 存儲引擎的是性能,那么雙寫帶給 InnoDB 存儲引擎的是數(shù)據(jù)的可靠性。當數(shù)據(jù)庫宕機時,可能數(shù)據(jù)庫正在寫一個頁面,而這個頁只寫了一部分(比如 16K 的頁,只寫了前 4K 的頁),我們稱之為寫失效。在 InnoDB 存儲引擎未使用 double write 之前,曾出現(xiàn)過因為部分寫失效而導致數(shù)據(jù)丟失的情況。

InnoDB 存儲引擎 double write 的體系架構如下圖所示:

雙寫緩存(double write)示意圖

double write 由兩部分組成:一部分是內(nèi)存中的 double write buffer,大小為 2MB。另一部分是物理磁盤上共享表空間中連續(xù)的 128 個頁,即兩個區(qū),大小同樣為 2MB。

當緩沖池的臟頁刷新時,并不直接寫磁盤,而是會他通過 memcpy 函數(shù)將臟頁先拷貝到內(nèi)存中的 doublewrite buffer,之后通過 doublewrite buffer 再分兩次,每次寫入 1MB 到共享表空間的物理磁盤上,然后馬上調(diào)用 fsync 函數(shù),同步磁盤,避免緩沖寫帶來的問題。在這個過程中,因為 doublewrite 頁是連續(xù)的,因此這個過程是順序?qū)?,開銷不是很大。

在完成 doublewrite 頁的寫入之后,再將 doublewrite buffer 中的頁寫入各個表空間文件中,此時的寫入則是離散的。我們可以通過 show global status like 'innodb_dblwr%'\G 觀察具體情況。

所以說雙寫,其實指的是刷臟頁的時候,即會將臟頁數(shù)據(jù)寫入共享表空間的 2 個頁中,也會寫入具體的表空間中,共享表空間的數(shù)據(jù)起到備份作用。

如果操作系統(tǒng)在將頁寫入具體的表空間過程中崩潰了,在恢復過程中,InnoDB 存儲引擎可以從共享表空間中的 doublewrite 中找到該頁的一個副本,將其拷貝到表空間文件,再應用重做日志。

Undo 表空間(Undo Tablespaces)

undo Log 的數(shù)據(jù)默認在系統(tǒng)表空間 ibdata1 文件中,因為共享表空間不會自動收縮,也可以單獨創(chuàng)建一個 undo 表空間。

一些疑問,關于 rollback segment 與 undo log?

很多時候,我們不太懂,為啥一些說 undo log 在系統(tǒng)表空間,而官方的圖卻有 undo tablespace ?下面這段話,描述得很清楚,原因是版本變化!

Rollback Segment(rseg)稱為回滾段。Mysql5.6 之前 undo 默認記錄到系統(tǒng)表空間(ibdata),如果開啟了 innodb_file_per_table ,將放在每個表的.ibd 文件中。5.6 之后還可以創(chuàng)建獨立的 undo 表空間,8 之后更是默認打開獨立 undo 表空間,最低數(shù)量為 2,這樣才能保證至少一個 undo 表空間進行 truncate,一個 undo 表空間繼續(xù)使用。

每個 rollback Segment 中默認有 1024 個 undo log segment,mysql5.5 后 1 個 undo 表空間支持 128 個 rollback Segment。0 號 rollback Segment 默認在系統(tǒng)表空間 ibdata 中,1-32rollback Segment 在臨時表空間,33~128 在獨立 undo 表空間中(沒有打開則在系統(tǒng)表空間 ibdata 中,這樣系統(tǒng)表空間會太大),所以 1 個表空間最多支持 96*1024 個事務,超了就報錯啦。

一個 undo log segment 稱為 undo log 或 undo slot 或 undo;一個 undo log 對象對應多個 undo log record,也就是記錄的歷史版本。(Rollback segment -> 多個 Undo log segment (undo log) -> 多個 undo log record)。

疑問 2:mysql rollback segment 和 undo segment 區(qū)別

感覺這兩個應該是差不多的呀?

結論:Rollback segment 與 undo segment 是包含的關系,每個 Rollback segment 有 1024 個 undo segment。

undo log 有兩個作用:提供回滾和多個行版本控制 (MVCC)。

在數(shù)據(jù)修改的時候,不僅記錄了 redo,還記錄了相對應的 undo,如果因為某些原因?qū)е率聞帐』蚧貪L了,可以借助該 undo 進行回滾。

undo log 和 redo log 記錄物理日志不一樣,它是邏輯日志??梢哉J為當 delete 一條記錄時,undo log 中會記錄一條對應的 insert 記錄,反之亦然,當 update 一條記錄時,它記錄一條對應相反的 update 記錄。

rollback segment 稱為回滾段,每個回滾段中有 1024 個 undo log segment。

Redo Log 文件(Redo Log)

redo log 即重做日志,從字面意思來看,其表示可以將事情重做一遍的意思。而事實上,它確實是代表著這個意思。對于上文的更新語句 update t set c=c+1 where id = 2,我們的正常實現(xiàn)思路應該是:

找到 id 為 2 的記錄,取出其 c 字段的值。

將 c 字段的值加一,之后將 id 為 2 的字段的 c 字段更新。

但實際上 MySQL 并不是這么做的,因為上述這種實現(xiàn)方式雖然能實現(xiàn),但是每次都要去讀取磁盤查找記錄、寫入磁盤更新記錄,整個過程的磁盤 IO 成本很高。為了提高效率,MySQL 使用了一種叫做 WAL(Write-Ahead Logging)的技術,即寫之前先記錄變更日志(redo log),等待合適的時間再將其變更應用到數(shù)據(jù)庫里。因為我們將操作記錄下來了,所以我們可以復現(xiàn)這個操作,這就好像我們將事情重現(xiàn)了一樣,因此叫 redo log。

使用 WAL 技術,上面這條更新語句的大致實現(xiàn)思路就變成了:

記錄下更新操作日志:其要將 id 為 2 的記錄的 c 字段加 1。

某個時刻,MySQL 數(shù)據(jù)庫應用這個 redo log 日志,將數(shù)據(jù)庫 id 為 2 的記錄的 c 字段加 1。

注意:redo log 并不會應用于磁盤的表空間,而是在重啟時應用于內(nèi)存表空間緩存,用于實現(xiàn) crash-safe。

可以看到,使用 WAL 技術的方式,可以不需要去讀寫磁盤,極大提高了執(zhí)行效率。

我們舉一個很形象的例子來理解 WAL 技術。想象有一個酒館,生意非常好,老板也愿意賒賬。每次別人想要賒賬,老板都得去翻賬本,看看這個人有沒有賒過賬,有賒賬的話就需要在原來的賒賬金額上再加上本次消費的金額。

在平時酒館人不多的時候,這種方式還是可以應付應付的。但是一旦到了酒館高峰期的時候,每個人都等著結賬,這時候再用這種方式去結賬,很可能讓客戶等太久,引起民憤。于是老板想了個辦法:我不去賬本上找誰賒賬了,我直接在黑板上記錄下誰賒賬了多少錢。例如:張三賒賬 3 塊銀元,李四賒賬 4 塊銀元。

等生意沒那么忙的時候,老板拿出賬本,將粉板上的變更記錄進賬本:哦,之前張三賒賬了 4 塊銀元,現(xiàn)在又賒賬了 3 塊銀元,所以張三現(xiàn)在總共賒賬 7 塊銀元。在這個例子中,賬本就相當于我們的 MySQL 數(shù)據(jù)庫,粉板就相當于我們的 redo log,它將消費記錄保存下來。

獨占表空間(File-Per-Table Tablespaces)

如果我們將 innodb_file_per_table 設置為 on,則每個表將獨立地產(chǎn)生一個表空間文件,以 ibd 結尾,數(shù)據(jù)、索引、表的內(nèi)部數(shù)據(jù)字典信息都將保存在這個單獨的表空間文件中。但是還有一些信息,例如 undo log 信息,還是會保存在系統(tǒng)表空間中。例如我們在 test 數(shù)據(jù)庫中創(chuàng)建了一個名為 user 的表,那么就會在 MySQL 的數(shù)據(jù)文件夾的 test 文件夾下,有一個名為 user.ibd 文件。

通用表空間(General Tablespaces)

通用表空間是后續(xù)的 MySQL 推出的表空間,其與系統(tǒng)表空間類似,可以用于存儲表的數(shù)據(jù)和索引。其作用是可以將一些業(yè)務邏輯不同的表,存放在這個通用表空間中,從而達到物理隔離的作用。

臨時表空間(Temporary Tablespaces)

存儲臨時表的數(shù)據(jù),包括用戶創(chuàng)建的臨時表,和磁盤的內(nèi)部臨時表。對應數(shù)據(jù)目錄下的 ibtmp1 文件。當數(shù)據(jù)服務器正常關閉時,該表空間被刪除,下次重新產(chǎn)生。

參考資料:

  • MySQL :: MySQL 5.7 Reference Manual :: 14.6.3 Tablespaces
  • InnoDB 內(nèi)存結構和磁盤結構_wang2963973852 的博客 - CSDN 博客
  • [Mysql] 漫游 undo log | 土川的自留地
  • 詳細分析 MySQL 事務日志 (undo log) - 裸奔的小鴕鳥 - 博客園
責任編輯:武曉燕 來源: 陳樹義
相關推薦

2021-03-11 08:51:00

存儲面試位置

2021-12-02 08:19:06

MVCC面試數(shù)據(jù)庫

2021-05-20 08:54:16

Go面向對象

2020-04-16 08:22:11

HTTPS加解密協(xié)議

2010-08-23 15:06:52

發(fā)問

2021-06-03 08:55:54

分布式事務ACID

2020-08-10 07:58:18

異步編程調(diào)用

2020-12-03 07:39:50

HashMap底層數(shù)據(jù)

2022-10-17 00:04:30

索引SQL訂單

2022-04-10 18:10:24

CURD鏈表

2021-08-28 09:06:11

Dubbo架構服務

2021-04-14 18:58:01

虛擬機 Java內(nèi)存

2021-09-29 19:17:51

編碼URLEncodeGBK

2022-04-01 07:52:42

JavaScript防抖節(jié)流

2021-05-08 07:53:33

面試線程池系統(tǒng)

2021-05-19 08:17:35

秒殺場景高并發(fā)

2020-12-01 11:50:49

數(shù)據(jù)庫Redis面試

2022-10-08 00:08:00

apiESFacebook

2022-02-09 09:37:54

ReactorNettyI/O

2020-10-26 07:07:50

線程安全框架
點贊
收藏

51CTO技術棧公眾號