得物面試:MySQL爲何需要4M來雙寫?爲什麼redo不雙寫?

文章很長,且持續更新,建議收藏起來,慢慢讀!瘋狂創客圈總目錄 博客園版 爲您奉上珍貴的學習資源 :

免費贈送 :《尼恩Java面試寶典》 持續更新+ 史上最全 + 面試必備 2000頁+ 面試必備 + 大廠必備 +漲薪必備
免費贈送 :《尼恩技術聖經+高併發系列PDF》 ,幫你 實現技術自由,完成職業升級, 薪酬猛漲!加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷1)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷2)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領
免費贈送 經典圖書:《Java高併發核心編程(卷3)加強版》 面試必備 + 大廠必備 +漲薪必備 加尼恩免費領

免費贈送 資源寶庫: Java 必備 百度網盤資源大合集 價值>10000元 加尼恩領取


得物面試:MySQL爲何需要4M來雙寫?爲什麼redo不雙寫?

尼恩說在前面

在40歲老架構師 尼恩的讀者交流羣(50+)中,最近有小夥伴拿到了一線互聯網企業如得物、阿里、滴滴、極兔、有贊、shein 希音、百度、網易的面試資格,遇到很多很重要的面試題:

MySQL爲何需要4M來雙寫?爲什麼redo不雙寫?

說說MySQL雙寫緩衝區?

小夥伴 沒有回答好,導致面試掛了,來求助尼恩,如何才能回答得很漂亮, 讓面試官刮目相看、口水直流。

MySQL雙寫, 尼恩在自己的社羣裏邊問了一下, 很多小夥伴都沒有聽過說。

所以,尼恩給大家做一下系統化、體系化的梳理,使得大家內力猛增,可以充分展示一下大家雄厚的 “技術肌肉”,讓面試官愛到 “不能自已、口水直流”,然後實現”offer直提”。

當然,這道面試題,以及參考答案,也會收入咱們的 《尼恩Java面試寶典》V131版本PDF集羣,供後面的小夥伴參考,提升大家的 3高 架構、設計、開發水平。

最新《尼恩 架構筆記》《尼恩高併發三部曲》《尼恩Java面試寶典》的PDF,請到公衆號【技術自由圈】獲取

本文目錄

InnoDB 存儲引擎

InnoDB 存儲引擎最早由 Innobase Oy 公司開發(屬第三方存儲引擎)。

從 MySQL 5.5 版本開始作爲表的默認存儲引擎。該存儲引擎是第一個完整支持 ACID 事務的 MySQL 存儲引擎,特點是行鎖設計、支持 MVCC、支持外鍵、提供一致性非鎖定讀,非常適合 OLTP 場景的應用使用。

目前也是應用最廣泛的存儲引擎。

InnoDB 存儲引擎架構包含內存結構和磁盤結構兩大部分

MySQL 8.0 版本,總體架構圖如下:

注意:請點擊圖像以查看清晰的視圖!

MySQL 5.5 版本,總體架構圖如下:

注意:請點擊圖像以查看清晰的視圖!

InnoDB 存儲結構

1、磁盤結構

1.1 表空間 Tablespaces

InnoDB 存儲引擎的邏輯存儲結構是將所有的數據都被邏輯地放在了一個空間中,這個空間中的文件就是實際存在的物理文件(.ibd 文件),即表空間。

默認情況下,一個數據庫表佔用一個表空間,表空間可以看做是 InnoDB 存儲引擎邏輯結構的最高層,所以的數據都存放在表空間中,例如:表對應的數據、索引、insert buffer bitmap undo 信息、insert buffer 索引頁、double write buffer files 等都是放在共享表空間中的。

表空間分爲:

  • 系統表空間 (ibdata1 文件)(共享表空間)
  • 臨時表空間
  • 常規表空間
  • Undo 表空間
  • file-per-table 表空間 (獨立表空間)。

系統表空間又包括雙寫緩衝區 (Doublewrite buffer)、Change Buffer 等

系統表空間 System Tablespace

系統表空間可以對應文件系統上一個或多個實際的文件,

默認情況下, InnoDB 會在數據目錄下創建一個名爲.ibdata1的文件,大小爲 12M

這個.ibdata1文件就是對應的系統表空間在文件系統上的表示。

.ibdata1這個文件是可以自擴展的,當不夠用的時候它會自己增加文件大小。

需要注意的一點是,在一個 MySQL 服務器中,系統表空間只有一份。

從 MySQL5.5.7 到 MySQL5.6.6 之間的各個版本中,我們表中的數據都會被默認存儲到這個系統表空間。

show variables like '%innodb_data_file_path%'

獨立表空間

在 MySQL5.6.6 以及之後的版本中, InnoDB 並不會默認的把各個表的數據存儲到系統表空間中,而是爲每一個表建立一個獨立表空間,也就是說我們創建了多少個表,就有多少個獨立表空間。

使用獨立表空間來存儲表數據的話,會在該表所屬數據庫對應的子目錄下創建一個表示該獨立表空間的文件,文件名和表名相同,只不過添加了一個.ibd 的擴展名而已。

show variables like '%innodb_file_per_table%'

獨立表空間只是存放數據、索引和插入緩衝 Bitmap 頁,其他類的數據如回滾(undo)信息、插入緩衝索引頁、系統事務信息、二次寫緩衝等還是存放在原來的系統表空間。

其他類型的表空間

隨着 MySQL 的發展,除了上述兩種表空間之外,現在還新提出了一些不同類型的表空間,比如通用表空間 (general tablespace)、undo 表空間 (undo tablespace)、臨時表空間 (temporary tablespace) 等

表空間結構

表空間又由段 (segment)、區 ( extent)、頁 (page) 組成,頁是 InnoDB 磁盤管理的最小單位。

在我們執行 sql 時,不論是查詢還是修改,mysql 總會把數據從磁盤讀取內內存中,而且在讀取數據時,不會單獨加在一條數據,而是直接加載數據所在的數據頁到內存中。

表空間本質上就是一個存放各種頁的頁面池。

注意:請點擊圖像以查看清晰的視圖!

「page頁」是 InnoDB 管理存儲空間的基本單位,也是內存和磁盤交互的基本單位。

也就是說,哪怕你需要 1 字節的數據,InnoDB 也會讀取整個頁的數據,

InnoDB 有很多類型的頁,它們的用處也各不相同。

比如:有存放 undo 日誌的頁、有存放 INODE 信息的頁、有存放 Change Buffer 信息的頁、存放用戶記錄數據的頁(索引頁)等等。

InnoDB 默認的頁大小是 16KB,在初始化表空間之前可以在配置文件中進行配置,一旦數據庫初始化完成就不可再變更了。

SHOW VARIABLES LIKE 'innodb_page_size'

1.2 重寫日誌 redo log 文件

redo log 記錄數據庫的變更,數據庫崩潰後,會從 redo log 獲取事務信息,進行系統恢復。

redo log 在磁盤上表現爲 ib_logfile0 和 ib_logfile1 兩個文件。

MySQL 會在事務的提交前將 redo 日誌刷新回磁盤。

在同一時間提交的事務,會採用組提交(group commit)的方式一次性刷新回磁盤。從而避免一個事務刷新一次磁盤,提高性能。

1.3 Double Write Files 雙寫緩衝文件

double write 是保障 InnoDB 存儲引擎操作數據頁的可靠性。

double write 分爲兩部分組成,一部分在內存中的 double write buffer, 大小爲 2MB,另一部分是物理磁盤上共享表空間中連續的 128 個數據頁,即 2 個區大小(同樣是 2MB)。

InnoDB存儲引擎doublewrite架構

InnoDB存儲引擎doublewrite架構

注意:請點擊圖像以查看清晰的視圖!

2、內存結構

InnoDB 存儲引擎是基於磁盤存儲的,並將其中的記錄按照頁的方式進行管理,因此可將其視爲基於磁盤的數據庫系統(Disk-base Database)。

在數據庫中 CPU 速度與磁盤速度是有很大差距的,所以,基於磁盤的數據庫系統通常使用緩衝池技術來提高數據庫的整體性能。結構如圖所示:

注意:請點擊圖像以查看清晰的視圖!

2.1 緩存池 Buffer Pool

Buffer Pool 是 InnoDB 內存中的一塊佔比較大的區域,通過內存的速度來彌補磁盤速度慢對數據庫性能的影響。

在數據庫中進行讀取頁的操作,首先將從磁盤讀到的頁放在緩衝池中,這個過程稱爲將頁”FIX” 在緩衝池中,

下次再讀到相同的頁時,首先判斷該頁是否在緩衝池中,若在緩衝池中,直接讀取該頁,否則讀取磁盤上的頁。

對於數據庫中的頁的修改操作,首先修改在緩衝池中的頁,然後再以一定頻率刷新到磁盤上,

這裏需要注意的是,頁從緩衝池刷新回磁盤的操作並不是在每次頁發生更新時觸發,而是通過一種稱爲 Checkpoint 的機制刷新回磁盤。

緩存區緩存的數據頁類型有:索引頁,數據頁,undo 頁,插入緩衝(change buffer),自適應哈希索引(adaptive hash index),InnoDB 存儲鎖信息(lock info),數據字典信息(data dictionary)。數據頁和索引頁佔據了緩衝池很大部分。

2.2 寫緩衝 Change Buffer

在 MySQL5.5 之前,叫插入緩衝(Insert Buffer),只針對 INSERT 做了優化;

現在對 DELETE 和 UPDATE 也有效,叫做寫緩衝(Change Buffer)。

它是一種應用在非唯一普通索引頁(non-unique secondary index page)不在緩衝池中,對頁進行了寫操作,並不會立刻將磁盤頁加載到緩衝池,而僅僅記錄緩衝變更(Buffer Changes),等未來數據被讀取時,再將數據合併(Merge)恢復到緩衝池中的技術。寫緩衝的目的是降低寫操作的磁盤 IO,提升數據庫性能。

2.3 自適應散列索引 Adaptive Hash Index

自適應哈希索引用於優化對 BP 數據的查詢。

InnoDB 存儲引擎會監控對二級索引數據的查找,如果觀察到建立哈希索引可以帶來速度的提升 (最近連續被訪問三次的數據),則建立哈希索引,自適應哈希索引通過緩衝池的 B + 樹構造而來,因此建立的速度很快。

InnoDB 存儲引擎會自動根據訪問的頻率和模式來爲某些頁建立哈希索引。(在高負載系統下 AHI 容易產生資源的爭用,進而引起一些 bug 導致系統受影響甚至崩潰,故建議關閉該功能)

2.4 重做日誌緩衝區 rodo Log Buffer

重做日誌緩衝區,當在 MySQL 中對 InnoDB 表進行數據更改時,這些更改首先存儲在 InnoDB 日誌緩衝區的內存中,然後再寫入重做日誌(redo logs)的 InnoDB 日誌磁盤文件中。

rodo Log Buffer讓 MySQL 在崩潰的時候具有了恢復數據的能力,即在數據庫發生意外的時候,可以進行數據恢復;

日誌緩衝區 log buffer 是內存存儲區域,用於保存要寫入磁盤上的日誌文件的數據。

日誌緩衝區大小由 innodb_log_buffer_size 變量定義,默認大小爲 16MB。

日誌緩衝區的內容定期刷新到磁盤。

較大的日誌緩衝區可以運行大型事務,而無需在事務提交之前將重做日誌數據寫入磁盤。

因此,如果有更新,插入或刪除許多行的事務,則增加日誌緩衝區的大小可以節省磁盤 I/O。

回到問題:爲什麼需要redo log

先用一個表對redo log 、undo log、binlog三種日誌進行對比 介紹,方便大家理解, 下表涉及到redo log 、undo log、binlog三種日誌使用場景和文件等

注意:請點擊圖像以查看清晰的視圖!

undolog、redolog都是InnoDB引擎中的日誌,都是在Buffer Pool中,而binlog在Server層中,位於每條線程中,

redo log 、undo log、binlog三大日誌在磁盤中的的歸檔方式和文件都是不一樣的,之間的區別如下圖。

注意:請點擊圖像以查看清晰的視圖!

在InnoDB存儲引擎中,大部分redo log記錄的是物理日誌,具體來說,就是記錄某個數據頁做了什麼修改。

前面講到, 所有數據頁的讀寫操作都要通過buffer pool進行

  • innodb的讀操作:先從buffer pool中查看數據的數據頁是否存在,如果不存在,則將數據頁先從磁盤讀到buffer pool中然後在讀
  • innodb的寫操作:先把數據和日誌寫入buffer pool和log buffer,再由後臺線程以一定的頻率將buffer中的內存刷到磁盤。

buffer pool是一塊內存區域,是一種“降低磁盤訪問機制”,buffer pool緩存數據表和索引數據,吧磁盤上的數據加載到緩衝池,避免每次訪問都進行磁盤IO,起到加速訪問的作用。

buffer pool也是以頁爲存儲單位(與磁盤中數據頁索引頁單位一樣,默認大小16K),buffer pool底層採用鏈表的數據結構管理page。

寫操作的事務持久性由redo log落盤保證,buffer pool只是爲了提高讀寫效率。

當buffer pool中某頁的數據更改後,這個頁就變成了髒頁(因爲mysql爲了提升性能,避免頻繁的隨機IO,每次更改數據不是第一時間去更改磁盤數據,這就導致了buffer pool中的數據和磁盤數據不一致)

實際上, redo log日誌主要包括兩部分:

  • 一是在內存中重做日誌緩衝(redo log Buffer)易丟失,在內存中,
  • 二是重做日誌文件(redo log file),保存在磁盤中。

爲了保證髒頁落盤的安全性,防止斷電丟失等,會 WAL 預寫redo log Buffer 中的日誌到磁盤中。

WAL機制是什麼?

WAL,全稱是Write-Ahead Logging, 預寫日誌系統。指的是 MySQL 的寫操作並不是立刻更新到磁盤上,而是先記錄在日誌上,然後在合適的時間再更新到磁盤上。

MySQL真正使用WAL的原因是:磁盤的寫操作是隨機IO,比較耗性能,所以如果把每一次的更新操作都先寫入log中,那麼就成了順序寫操作,實際更新操作由後臺線程再根據log異步寫入。

通過WAL,這樣對於client端,延遲就降低了。

並且,由於順序寫入大概率是在一個磁盤塊內,這樣產生的IO次數也大大降低。

所以WAL的核心在於將隨機寫轉變爲了順序寫,降低了客戶端的延遲,提升了吞吐量。

使用redo log的好處主要2點:

  • 降低刷盤頻率:變更日誌先行存入redo log buffer,髒頁刷盤和redo log buffer刷盤時機可控;
  • 日誌佔用空間小:存儲表空間ID、頁號、偏移量和需要更新的值。

注意:請點擊圖像以查看清晰的視圖!

爲什麼寫數據需要Double write Buffer

MySQL程序是跑在Linux操作系統上的,理所當然要跟操作系統交互,

一般來說,MySQL中一頁數據是16kb,操作系統一個頁是 4kb,所以,mysql page 刷到磁盤,要寫4個文件系統裏的頁。

如圖所示:

注意:請點擊圖像以查看清晰的視圖!

需要注意的是,這個刷頁的操作並非原子操作,比如我操作系統寫到第二個頁的時候,Linux機器斷電了,這時候就會出現問題了。

上面的問題,也叫「Partial Page Write(部分頁寫入)」問題,或者叫做「頁數據損壞」。並且這種頁數據損壞靠 redo日誌是無法修復的。

爲啥 redo日誌是無法修復 部分頁寫入問題呢?

具體來說,和redo 的記錄內容有關。 redo log 的內容包括 存儲表空間ID、頁號、偏移量和需要更新的值。

也就是說,redo log日誌中記錄的是對頁的物理操作,而不是頁面的全量記錄,當發生「Partial Page Write(部分頁寫入)」問題時,如果出現問題的數據是未修改過的數據,此時redo日誌無能爲力

Doublewrite Buffer 就是爲了解決「頁數據損壞」 問題的。

Doublewrite Buffer 提供了數據頁的可靠性,雖然名字帶了Buffer,但實際上Doublewrite Buffer是「內存+磁盤」的結構。

Doublewrite Buffer 包括兩個部分:

  • 內存結構:Doublewrite Buffer內存結構由128個頁(Page)構成,大小是2MB。

  • 磁盤結構:Doublewrite Buffer磁盤結構在系統表空間上是128個頁(2個區,extend1和extend2),大小是2MB。

Doublewrite Buffer的原理是,再把數據頁寫到數據文件之前,InnoDB先把它們寫到一個叫「doublewrite buffer(雙寫緩衝區)」的共享表空間內,在寫doublewrite buffer完成後,InnoDB纔會把頁寫到數據文件適當的位置。

如果在寫頁的過程中發生意外崩潰,InnoDB會在doublewrite buffer中找到完好的page副本用於恢復。

Doublewrite Buffer原理

注意:請點擊圖像以查看清晰的視圖!

如上圖所示,當有數據頁要刷盤時:

  1. 頁數據先通過memcpy函數拷貝至內存中的Doublewrite Buffer中。
  2. Doublewrite Buffer的內存裏的數據頁,會fsync刷到Doublewrite Buffer的磁盤上,分兩次寫入磁盤共享表空間中(連續存儲,順序寫,性能很高),每次寫1MB。
  3. Doublewrite Buffer的內存裏的數據頁,再刷到數據磁盤存儲.ibd文件上(離散寫)。

所以在正常的情況下,MySQL寫數據頁時,會寫兩遍到磁盤上,

  • 第一遍是寫到doublewrite buffer,
  • 第二遍是寫到真正的數據文件中,這便是「Doublewrite」的由來。

如果操作系統在將頁寫入磁盤的過程中發生了崩潰,在恢復過程中,InnoDB存儲引擎可以從共享表空間中的Double write中找到該頁的一個副本,將其複製到表空間文件,再應用redo日誌。

Doublewrite Buffer相關參數

以下是一些與Doublewrite Buffer相關的參數及其含義:

  • innodb_doublewrite: 這個參數用於啓用或禁用雙寫緩衝區。設置爲1時啓用,設置爲0時禁用, 默認值爲1。
  • innodb_doublewrite_files: 這個參數定義了多少個雙寫文件被使用。默認值爲2,有效範圍從2到127。
  • innodb_doublewrite_dir: 這個參數指定了存儲雙寫緩衝文件的目錄的路徑。默認爲空字符串,表示將文件存儲在數據目錄中。
  • innodb_doublewrite_batch_size: 這個參數定義了每次批處理操作寫入的字節數。默認值爲0,表示InnoDB會選擇最佳的批量大小。
  • innodb_doublewrite_pages:這個參數定義了每個雙寫文件包含多少頁面。默認值爲128。

Doublewrite Buffer和redo log

在MySQL的InnoDB存儲引擎中,Redo log和Doublewrite Buffer共同工作以確保數據的持久性和恢復能力。

  1. 首先wal架構:當有一個DML(如INSERT、UPDATE)操作發生時, InnoDB會首先將這個操作寫入redo log(內存)。這些日誌被稱爲未檢查點(uncheckpointed)的redo日誌。

  2. 然後,在修改內存中相應的數據頁之後,需要將這些更改記錄在磁盤上。

    但是直接把這些修改的頁寫到其真正的位置可能會因發生故障導致頁部分更新,從而導致數據不一致。

    因此,InnoDB的做法是先將這些修改的頁按順序寫入doublewrite buffer。

    這就是爲什麼叫做 "doublewrite" —— 數據實際上被寫了兩次,先在doublewrite buffer,然後在它們真正的位置。

  3. 一旦這些頁被安全地寫入doublewrite buffer,它們就可以按原始的順序寫回到文件系統中。

    即使這個過程在寫回數據時發生故障,我們仍然可以從doublewrite buffer中恢復數據。

  4. 最後,當事務提交時,相關聯的redo log會被寫入磁盤。這樣即使系統崩潰,redo log也可以用來重播(replay)事務並恢復數據庫。

在系統恢復期間,InnoDB會檢查doublewrite buffer,並嘗試從中恢復損壞的數據頁。

如果doublewrite buffer中的數據是完整的,那麼InnoDB就會用doublewrite buffer中的數據來更新損壞的頁。

否則,如果doublewrite buffer中的數據不完整,InnoDB也有可能丟棄buffer內容,重新執行那條redo log以嘗試恢復數據。

所以,Redo log和Doublewrite Buffer的協作可以確保數據的完整性和持久性。如果在寫入過程中發生故障,我們可以從doublewrite buffer中恢復數據,並通過redo log來進行事務的重播。

總結

Doublewrite Buffer是InnoDB的一個重要特性,用於保證MySQL數據的可靠性和一致性。

它的實現原理是通過將要寫入磁盤的數據先寫入到Doublewrite Buffer中的內存緩存區域,然後再寫入到磁盤的兩個不同位置,來避免由於磁盤損壞等因素導致數據丟失或不一致的問題。

總的來說,Doublewrite Buffer對於改善數據庫性能和數據完整性起着至關重要的作用。儘管其引入了一些開銷,但在大多數情況下,這些成本都被其提供的安全性和可靠性所抵消。

說在最後

MySQL雙寫面試題,是非常常見的面試題。

以上的內容,如果大家能對答如流,如數家珍,基本上 面試官會被你 震驚到、吸引到。

在面試之前,建議大家系統化的刷一波 5000頁《尼恩Java面試寶典PDF》,並且在刷題過程中,如果有啥問題,大家可以來 找 40歲老架構師尼恩交流。

最終,讓面試官愛到 “不能自已、口水直流”。offer, 也就來了。

技術自由的實現路徑:

實現你的 架構自由:

喫透8圖1模板,人人可以做架構

10Wqps評論中臺,如何架構?B站是這麼做的!!!

阿里二面:千萬級、億級數據,如何性能優化? 教科書級 答案來了

峯值21WQps、億級DAU,小遊戲《羊了個羊》是怎麼架構的?

100億級訂單怎麼調度,來一個大廠的極品方案

2個大廠 100億級 超大流量 紅包 架構方案

… 更多架構文章,正在添加中

實現你的 響應式 自由:

響應式聖經:10W字,實現Spring響應式編程自由

這是老版本 《Flux、Mono、Reactor 實戰(史上最全)

實現你的 spring cloud 自由:

Spring cloud Alibaba 學習聖經》 PDF

分庫分表 Sharding-JDBC 底層原理、核心實戰(史上最全)

一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之間混亂關係(史上最全)

實現你的 linux 自由:

Linux命令大全:2W多字,一次實現Linux自由

實現你的 網絡 自由:

TCP協議詳解 (史上最全)

網絡三張表:ARP表, MAC表, 路由表,實現你的網絡自由!!

實現你的 分佈式鎖 自由:

Redis分佈式鎖(圖解 - 秒懂 - 史上最全)

Zookeeper 分佈式鎖 - 圖解 - 秒懂

實現你的 王者組件 自由:

隊列之王: Disruptor 原理、架構、源碼 一文穿透

緩存之王:Caffeine 源碼、架構、原理(史上最全,10W字 超級長文)

緩存之王:Caffeine 的使用(史上最全)

Java Agent 探針、字節碼增強 ByteBuddy(史上最全)

實現你的 面試題 自由:

4800頁《尼恩Java面試寶典 》 40個專題

免費獲取11個技術聖經PDF:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章