從一條更新SQL的執行過程窺探InnoDB之REDOLOG

京東雲 發表於 2022-08-18
SQL

1 前言

資料庫為了取得更好的讀寫效能,InnoDB會將資料快取在記憶體中(InnoDB Buffer Pool),對磁碟資料的修改也會落後於記憶體,這時如果程式或機器崩潰,會導致記憶體資料丟失,為了保證資料庫本身的一致性和永續性,InnoDB維護了REDO LOG。修改Page之前需要先將修改的內容記錄到REDO中,並保證REDO LOG早於對應的Page落盤,也就是常說的WAL,Write Ahead Log。當故障發生導致記憶體資料丟失後,InnoDB會在重啟時,透過重放REDO,將Page恢復到崩潰前的狀態。

2 MYSQL更新語句的執行過程

2.1 MYSQL的體系結構

大體來說,MySQL 可以分為 客戶端、Server層和儲存引擎層三大部分,如圖所示。

Server 層包括聯結器、查詢快取、分析器、最佳化器、執行器等,涵蓋 MySQL 的大多數核心服務功能,以及所有的內建函式(如日期、時間、數學和加密函式等),所有跨儲存引擎的功能都在這一層實現,比如儲存過程、觸發器、檢視等。

儲存引擎層負責資料的儲存和提取。其架構模式是外掛式的,支援 InnoDB、MyISAM、Memory 等多個儲存引擎。現在最常用的儲存引擎是 InnoDB,它從 MySQL 5.5.5 版本開始成為了預設儲存引擎。

從一條更新SQL的執行過程窺探InnoDB之REDOLOG

2.2 更新SQL的執行

當我們執行一條更新SQL時是如何執行的呢,下面執行一條簡單的SQL更新語句(預設儲存引擎InnoDB)

update T set c=c+1 where ID=2;

第一步:聯結器
先透過聯結器連線到這個資料庫上。聯結器負責跟客戶端建立連線、校驗使用者名稱密碼的正確性,同時獲取該使用者的許可權放到快取中、維持和管理連線

第二步:快取
連線建立完成後,如果執行的是SELECT查詢 語句會查詢快取中是否存在該SQL的結果集,如果存在結果則再校驗使用者表和資料的許可權最終將查詢到的結果返回。如果是UPDATE,DELETE等更新操作,那麼跟這個表有關的查詢快取會置為失效,所以這條語句就會把表 T 上所有快取結果都清空。

第三步:分析器
如果沒有命中查詢快取,就要開始真正執行語句了。首先,MySQL 需要知道你要做什麼,因此需要對 SQL 語句做解析。
分析器先會做“詞法分析”。你輸入的是由多個字串和空格組成的一條 SQL 語句,MySQL 需要識別出裡面的字串分別是什麼,代表什麼。例如該語句中c列在表T中是否存在等。
做完了這些識別以後,就要做“語法分析”。根據詞法分析的結果,語法分析器會根據語法規則,判斷你輸入的這個 SQL 語句是否滿足 MySQL 語法。該SQL語句中的update、where 等是否符合SQL語法

第四步:最佳化器
經過了分析器,MySQL 就知道你要做什麼了。在開始執行之前,還要先經過最佳化器的處理。最佳化器是在表裡面有多個索引的時候,決定使用哪個索引;或者在一個語句有多表關聯(join)的時候,決定各個表的連線順序;最佳化器決定要使用 ID 這個索引。指定索引也就指定了後面的執行器需要呼叫儲存引擎的哪個介面進行執行。

第五步:執行器
MySQL 透過分析器知道了你要做什麼,透過最佳化器知道了該怎麼做,於是就進入了執行器階段,開始執行語句。開始執行的時候,要先判斷一下你對這個表 T 有沒有執行查詢的許可權,如果沒有,就會返回沒有許可權的錯誤。執行器負責具體執行,找到這一行,然後更新。

從一條更新SQL的執行過程窺探InnoDB之REDOLOG

2.3 InnoDB儲存引擎引入REDOLOG

Mysql本身有自己的日誌記錄binlog(歸檔日誌:分為row,statement,MIX 三種模式),但是隻依靠binlog是沒有crash-safe能力的,所以在儲存引擎層InnoDB使用另外一套日誌系統redolog來實現crash-safe能力。同時為了取得更好的讀寫效能,InnoDB會將資料快取在記憶體中(InnoDB Buffer Pool),對磁碟資料的修改也會落後於記憶體,這時如果程式或機器崩潰,會導致記憶體資料丟失,從而保證資料庫本身的一致性和永續性。修改Page之前需要先將修改的內容記錄到REDO中,並保證REDO LOG早於對應的Page落盤,也就是常說的WAL,Write Ahead Log。當故障發生導致記憶體資料丟失後,InnoDB會在重啟時,透過重放REDO,將Page恢復到崩潰前的狀態。

那麼我們需要什麼樣的REDO呢?

首先,REDO的維護增加了一份寫盤資料,同時為了保證資料正確,事務只有在他的REDO全部落盤才能返回使用者成功,REDO的寫盤時間會直接影響系統吞吐,顯而易見,REDO的資料量要儘量少。其次,系統崩潰總是發生在始料未及的時候,當重啟重放REDO時,系統並不知道哪些REDO對應的Page已經落盤,因此REDO的重放必須可重入,即REDO操作要保證冪等。最後,為了便於透過併發重放的方式加快重啟恢復速度,REDO應該是基於Page的,即一個REDO只涉及一個Page的修改。
資料量小是Logical Logging的優點,而冪等以及基於Page正是Physical Logging的優點。InnoDB採取了一種稱為Physiological Logging的方式,來兼得二者的優勢。所謂Physiological Logging,就是以Page為單位,但在Page內以邏輯的方式記錄。舉個例子,一種作用於Page型別的REDOLOG中記錄了對Page中一個Record的修改,方法如下:

(Page ID,Record Offset,(Filed 1, Value 1) … (Filed i, Value i) … )

其中,PageID指定要操作的Page頁,Record Offset記錄了Record在Page內的偏移位置,後面的Field陣列,記錄了需要修改的Field以及修改後的Value。

2.4 REDOLOG的記錄內容

從一條更新SQL的執行過程窺探InnoDB之REDOLOG

其中Type就是記錄的作用物件(根據REDO記錄不同的作用物件,可劃分為三個大類:作用於Page,作用於Space以及提供額外資訊的Logic型別),Space ID和Page Number唯一標識一個Page頁,這三項是所有REDO記錄都需要有的頭資訊。

後面的是MLOG_REC_UPDATE_IN_PLACE型別(作用於Page)獨有的,其中Record Offset用給出要修改的記錄在Page中的位置偏移,Update Field Count說明記錄裡有幾個Field要修改,緊接著對每個Field給出了Field編號(Field Number),資料長度(Field Data Length)以及資料(Filed Data)

3 總結

透過對一個更新SQl語句執行過程的跟蹤,瞭解熟悉Mysql的執行過程,瞭解REDOLOG的資料的內容格式,從根本上理解REDOLOG的設計的思路和原理,為以後的應用系統的開發和設計提供思想上的借鑑和實踐。