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

MySQL核心揭秘:從查詢到修改,徹底理解 Undo Log、Redo Log、Binlog 與 ACID 的關(guān)系

數(shù)據(jù)庫 MySQL
作為MySQL特有的二進(jìn)制日志,支持?jǐn)?shù)據(jù)復(fù)制和恢復(fù),進(jìn)一步增強(qiáng)了數(shù)據(jù)的持久性和系統(tǒng)的可擴(kuò)展性。理解和掌握這些日志機(jī)制,不僅有助于優(yōu)化數(shù)據(jù)庫性能,提升事務(wù)處理效率,還能在面對系統(tǒng)故障時快速進(jìn)行數(shù)據(jù)恢復(fù),確保業(yè)務(wù)的連續(xù)性和數(shù)據(jù)的安全性。

1.前言

在當(dāng)今數(shù)據(jù)驅(qū)動的時代,數(shù)據(jù)庫系統(tǒng)作為信息存儲和管理的核心組件,其性能和可靠性直接影響著應(yīng)用的穩(wěn)定性和用戶體驗。MySQL,作為最流行的開源關(guān)系型數(shù)據(jù)庫管理系統(tǒng)之一,被廣泛應(yīng)用于各類互聯(lián)網(wǎng)應(yīng)用中。然而,許多開發(fā)者和數(shù)據(jù)庫管理員對其內(nèi)部機(jī)制知之甚少,特別是在事務(wù)處理和日志管理方面。

事務(wù)的ACID特性(原子性 Atomicity、一致性 Consistency、隔離性 Isolation 和持久性 Durability)是保障數(shù)據(jù)庫操作可靠性和數(shù)據(jù)一致性的關(guān)鍵。為了實(shí)現(xiàn)這些特性,MySQL引入了多種日志機(jī)制,其中Undo Log、Redo Log和 Binlog 扮演著至關(guān)重要的角色。這些日志不僅確保了事務(wù)的正確執(zhí)行和系統(tǒng)的高可用性,還為數(shù)據(jù)恢復(fù)和復(fù)制提供了堅實(shí)的基礎(chǔ)。

本文將帶您深入探討MySQL中Undo Log、Redo Log和Binlog的核心機(jī)制,全面解析它們?nèi)绾螀f(xié)同工作以保障事務(wù)的ACID特性。從查詢優(yōu)化到數(shù)據(jù)修改,我們將揭示這些日志在實(shí)際應(yīng)用中的具體實(shí)現(xiàn)方式和作用機(jī)制,幫助您全面掌握MySQL事務(wù)管理的內(nèi)部原理,提升數(shù)據(jù)庫設(shè)計和運(yùn)維的能力。

2.MySQL的架構(gòu)

圖片圖片

從圖中可以看出,MySQL 的架構(gòu)可以大致劃分為四個層次:連接層、服務(wù)層、存儲引擎層和文件系統(tǒng)層。

  • 連接層:負(fù)責(zé)對來自客戶端的連接進(jìn)行權(quán)限驗證,并將連接信息存入連接池中,方便后續(xù)的連接復(fù)用。
  • 服務(wù)層:主要負(fù)責(zé) SQL 語句的解析與優(yōu)化,還包括查詢緩存和 MySQL 內(nèi)置函數(shù)的實(shí)現(xiàn)。
  • 存儲引擎層:提供多種可插拔的存儲引擎,允許我們通過不同的引擎進(jìn)行數(shù)據(jù)的存取操作。存儲引擎使得 MySQL 能夠直接與硬盤上的數(shù)據(jù)和日志進(jìn)行交互,用戶可以根據(jù)需求選擇合適的引擎。
  • 文件系統(tǒng)層:這一層主要包括日志文件、數(shù)據(jù)文件及與 MySQL 相關(guān)的其他程序。在這四個層次中,服務(wù)層和存儲引擎層構(gòu)成了架構(gòu)的核心。服務(wù)層負(fù)責(zé)處理 MySQL 的核心邏輯,而存儲引擎層則直接負(fù)責(zé)數(shù)據(jù)的存取操作。

3.Server服務(wù)層的功能

3.1 查詢解析與優(yōu)化

  • 語法解析:將客戶端發(fā)送的 SQL 查詢轉(zhuǎn)化為可理解的內(nèi)部結(jié)構(gòu),檢查 SQL 語句是否符合 MySQL 語法規(guī)則。后續(xù)步驟的傳遞和處理就是基于這個結(jié)構(gòu)的。
  • 查詢重寫:服務(wù)層會將一些查詢優(yōu)化為等效的、執(zhí)行效率更高的形式。
  • 查詢優(yōu)化:MySQL 服務(wù)層有一個查詢優(yōu)化器,會基于數(shù)據(jù)統(tǒng)計信息,選擇最優(yōu)的執(zhí)行計劃。包括表的連接順序、索引的選擇等。

3.2 查詢緩存

MySQL 服務(wù)層可以將一些查詢的結(jié)果緩存起來,尤其是頻繁執(zhí)行的查詢。如果客戶端請求的查詢已經(jīng)存在于緩存中,MySQL 可以直接從緩存中返回結(jié)果,而無需重新執(zhí)行查詢。但是緩存在生產(chǎn)環(huán)境一般是不開啟的,除非經(jīng)常有SQL一模一樣的查詢。緩存嚴(yán)格要求2次SQL請求要完全一樣。SQL語句,連接的數(shù)據(jù)庫,協(xié)議版本,字符集等因素不一樣,都會導(dǎo)致緩存失效。從8.0版本開始,MySQL不在使用緩存。

3.3 SQL 執(zhí)行

在查詢解析和優(yōu)化之后,服務(wù)層負(fù)責(zé)將 SQL 語句執(zhí)行。服務(wù)層會將解析后的查詢計劃傳遞給存儲引擎層,存儲引擎負(fù)責(zé)實(shí)際的數(shù)據(jù)操作。

4.Server服務(wù)層的核心組件

MySQL服務(wù)層有三個核心組件,分別是解析器,優(yōu)化器,執(zhí)行器。每個組件在查詢的過程中分別代表著不同的角色,下面將分別介紹這三者的作用:

4.1 解析器

解析器是 SQL 查詢執(zhí)行的第一步,它的主要職責(zé)是將用戶發(fā)送的 SQL 語句解析成數(shù)據(jù)庫能夠理解和處理的內(nèi)部結(jié)構(gòu)。這個過程包括以下幾個子階段:

  • 詞法分析:解析器首先對 SQL 語句進(jìn)行詞法分析。詞法分析的任務(wù)是將 SQL 語句拆分成一系列有意義的“單詞”或“標(biāo)記”(Tokens),例如:表名,列名,操作符,關(guān)鍵字等。這個階段的輸出是一個由標(biāo)記組成的列表,為后續(xù)的語法分析提供基礎(chǔ)。
  • 語法分析:語法分析的任務(wù)是根據(jù) SQL 語法規(guī)則將標(biāo)記組合成一個層次化的結(jié)構(gòu),通常是 解析樹(Parse Tree)抽象語法樹(Abstract Syntax Tree)。解析樹的每個節(jié)點(diǎn)代表了 SQL 語句的一個語法結(jié)構(gòu)單元。在這一階段,MySQL 會檢查 SQL 語句的語法是否正確,并生成語法樹。如果 SQL 語法不正確,解析器會報錯并拒絕執(zhí)行。
  • 語義分析:語義分析主要是檢查 SQL 語句中的每個對象是否符合數(shù)據(jù)庫的實(shí)際情況。如表名、字段名是否存在,用戶是否對相關(guān)表和列擁有執(zhí)行權(quán)限,數(shù)據(jù)類型是否匹配等。

解析器將 SQL 語句從文本形式轉(zhuǎn)化為數(shù)據(jù)庫可以理解的內(nèi)部表示。它完成了從詞法、語法到語義的分析,并確保 SQL 語句符合語法和語義規(guī)則。

4.2 優(yōu)化器

優(yōu)化器的主要任務(wù)是對 SQL 查詢進(jìn)行優(yōu)化,生成一個最優(yōu)的執(zhí)行計劃,從而提高查詢性能。優(yōu)化器的工作基于查詢的解析樹和元數(shù)據(jù),它會嘗試在不同的查詢執(zhí)行策略中選擇效率最高的一個。

4.2.1 邏輯優(yōu)化

在這一階段,優(yōu)化器會進(jìn)行一些邏輯層面的優(yōu)化,主要目的是通過調(diào)整 SQL 語句的結(jié)構(gòu)來提高查詢效率。這些優(yōu)化包括:

  • 消除冗余的子查詢:將某些子查詢轉(zhuǎn)換為連接或合并查詢。
  • 重寫查詢:比如將 OR 條件轉(zhuǎn)換為 UNION 操作。
  • 查詢合并:將多個查詢合并成一個查詢。
  • 移除不必要的操作:例如消除不需要的 ORDER BY 或 DISTINCT。

4.2.2 物理優(yōu)化

優(yōu)化器根據(jù)數(shù)據(jù)庫的具體執(zhí)行引擎、索引、統(tǒng)計信息等做出的決策。這個階段會根據(jù)優(yōu)化器評估的成本模型選擇合適的執(zhí)行計劃。具體的優(yōu)化措施包括:

  • 選擇合適的連接方式:比如選擇 Nested Loop Join、Hash Join 或 Sort Merge Join。
  • 選擇索引:通過選擇合適的索引來加速數(shù)據(jù)訪問。
  • 選擇合適的排序方式:通過使用索引掃描或臨時表來避免全表掃描。

優(yōu)化器會使用基于成本的模型(Cost-Based Optimization)來評估每種查詢執(zhí)行計劃的成本,選擇成本最低的執(zhí)行計劃。其核心是通過計算不同執(zhí)行計劃的資源消耗(如 CPU 時間、I/O 操作等),并選出最優(yōu)的執(zhí)行策略。

優(yōu)化器的目標(biāo)是通過多種優(yōu)化策略來降低查詢的執(zhí)行成本,生成一個盡可能高效的執(zhí)行計劃。它在邏輯層面和物理層面對 SQL 查詢進(jìn)行優(yōu)化,以減少查詢執(zhí)行所需的資源。

4.3 執(zhí)行器

執(zhí)行器是 SQL 查詢執(zhí)行過程的最后一步,負(fù)責(zé)根據(jù)優(yōu)化器生成的執(zhí)行計劃來實(shí)際執(zhí)行 SQL 查詢,返回查詢結(jié)果或修改數(shù)據(jù)庫的狀態(tài)。

4.3.1 權(quán)限檢查

在執(zhí)行之前,執(zhí)行器會首先檢查用戶是否有權(quán)限執(zhí)行相應(yīng)的操作。如果沒有權(quán)限,則返回錯誤信息。

4.3.2 執(zhí)行計劃的執(zhí)行

執(zhí)行器根據(jù)優(yōu)化器選擇的執(zhí)行計劃一步步執(zhí)行 SQL 操作。執(zhí)行器的主要工作包括:

  • 表掃描:根據(jù)查詢條件決定是否使用索引、是否全表掃描。
  • 連接操作:根據(jù)優(yōu)化器選擇的連接方式(如嵌套循環(huán)連接、哈希連接等)執(zhí)行表之間的數(shù)據(jù)合并。
  • 排序和聚合:執(zhí)行查詢中的 ORDER BY、GROUP BY 等操作。
  • 數(shù)據(jù)返回:查詢結(jié)果被返回給用戶,修改操作則會提交事務(wù)。

4.3.3 事務(wù)管理

對于涉及數(shù)據(jù)修改的 SQL(如 INSERT、UPDATE、DELETE 等),執(zhí)行器還需要管理事務(wù)的提交和回滾操作,確保數(shù)據(jù)的一致性和持久性。這些操作會與 MySQL 的日志系統(tǒng)(Undo Log、Redo Log、Binlog) 密切交互,確保事務(wù)的 ACID 屬性。

執(zhí)行器根據(jù)優(yōu)化器生成的執(zhí)行計劃實(shí)際執(zhí)行 SQL 查詢,完成數(shù)據(jù)操作,返回查詢結(jié)果或更新數(shù)據(jù)庫狀態(tài)。它是查詢執(zhí)行的最后環(huán)節(jié),直接與 MySQL 的存儲引擎進(jìn)行交互。

5.MySQL查詢語句的執(zhí)行過程

MySQL一條SQL語句的執(zhí)行過程可以大致分為以下幾個步驟:圖片

  • 首先,通過連接器,客戶端與MySQL服務(wù)器建立連接,并完成身份認(rèn)證和權(quán)限驗證過程。在此過程中,客戶端需要提供用戶名和密碼以證明其合法性,服務(wù)器則會對這些信息進(jìn)行核對。
  • 檢查是否開啟緩存。MySQL 8.0之前,Query Cache 確實(shí)會緩存完全相同的查詢結(jié)果,以便重復(fù)執(zhí)行相同查詢時直接返回緩存數(shù)據(jù)。然而,MySQL 8.0及以后版本已經(jīng)完全棄用Query Cache,因此在MySQL 8.0及更高版本中這一步驟不在適用。
  • MySQL的解析器會對查詢語句進(jìn)行解析,檢查語法是否正確,并將查詢語句轉(zhuǎn)換為內(nèi)部數(shù)據(jù)結(jié)構(gòu)。預(yù)處理器則會根據(jù)MySQL的規(guī)則進(jìn)一步檢查解析樹是否合法,如檢查數(shù)據(jù)表或數(shù)據(jù)列是否存在等。
  • 優(yōu)化器會根據(jù)查詢語句的結(jié)構(gòu)、表的統(tǒng)計信息等因素,生成多個可能的執(zhí)行計劃,并通過成本估算器選出最優(yōu)的執(zhí)行計劃。這一步旨在提高查詢效率,降低資源消耗。
  • 執(zhí)行器按照優(yōu)化器選擇的執(zhí)行計劃,調(diào)用存儲引擎的API來執(zhí)行查詢。存儲引擎負(fù)責(zé)實(shí)際的數(shù)據(jù)存儲和檢索,根據(jù)執(zhí)行器的請求,讀取或?qū)懭霐?shù)據(jù)。
  • 存儲引擎負(fù)責(zé)實(shí)際的數(shù)據(jù)存儲和檢索工作,根據(jù)執(zhí)行器的請求,讀取或?qū)懭霐?shù)據(jù)。
  • 如果開啟了Query Cache且查詢結(jié)果能夠命中緩存,查詢結(jié)果會從緩存中直接返回。而如果沒有開啟Query Cache或緩存沒有命中,MySQL會直接返回查詢結(jié)果。

6.MySQL修改語句的執(zhí)行過程

我們簡單列舉一條SQL為例 update table set name=“張三” where id = 10。具體的還行流程如下圖:

圖片圖片

  • 找存儲引擎取到 id = 10 這一行記錄。
  • 根據(jù)主鍵索引樹找到這一行,如果 id = 10 這一行所在的數(shù)據(jù)頁本來就在內(nèi)存池(Buffer Pool)中,就直接返回給執(zhí)行器;否則,需要先從磁盤讀入內(nèi)存池,然后再返回。
  • 記錄Undo Log日志,對數(shù)據(jù)進(jìn)行備份,便于回滾。
  • 拿到存儲引擎返回的行記錄,把 name 字段設(shè)置為 “張三”,得到一行新的記錄,然后再調(diào)用存儲引擎的接口寫入這行新記錄。
  • 將這行新數(shù)據(jù)更新到內(nèi)存中,同時將這個更新操作記錄到 Redo Log 里面,為 Redo Log 中的事務(wù)打上 prepare 標(biāo)識。然后告知執(zhí)行器執(zhí)行完成了,隨時可以提交事務(wù)。
  • 生成這個操作的 Binlog,并把 Binlog 寫入磁盤。
  • 提交事務(wù)。
  • 把剛剛寫入的 Redo Log 狀態(tài)改成提交(commit)狀態(tài),更新完成。

以上只是一個簡單的case,方便我們能夠簡單的熟悉流程。接下來,我們對update過程中的全流程進(jìn)行梳理,具體的流程如下圖:

圖片圖片

  • 首先客戶端發(fā)送一條 SQL 語句到 Server 層的 SQL interface。
  • SQL interface 接到該請求后,先對該條語句進(jìn)行解析,驗證權(quán)限是否匹配,也就是在我們上文中講到的執(zhí)行器中在執(zhí)行。
  • 驗證通過以后,分析器會對該語句分析,是否語法有錯誤等。
  • 接下來是優(yōu)化器生成相應(yīng)的執(zhí)行計劃,選擇最優(yōu)的執(zhí)行計劃,然后是執(zhí)行器根據(jù)執(zhí)行計劃執(zhí)行這條語句。
  • 執(zhí)行器從Buffer Pool中獲取數(shù)據(jù)頁的數(shù)據(jù),如果數(shù)據(jù)頁沒有,需要從磁盤中進(jìn)行加載。
  • 開啟事務(wù),修改數(shù)據(jù)之前先記錄Undo Log,寫入Buffer Pool的Undo Page。
  • 開始更新數(shù)據(jù)頁中的記錄,被修改的數(shù)據(jù)頁稱為臟頁,修改會被記錄到內(nèi)存中的 Redo Log Buffer中,再刷盤到磁盤的Redo Log文件,此時事務(wù)是 perpare階段。
  • 這個時候更新就完成了,當(dāng)時臟頁不會立即寫入磁盤,而是由后臺線程完成,這里會用double write來保證臟頁刷盤的可靠性。
  • 通知Server層,可以正式提交數(shù)據(jù)了, 執(zhí)行器記錄Binlog cache,事務(wù)提交時才會將該事務(wù)中的Binlog刷新到磁盤中。
  • 這個時候Update語句完成了Buffer Pool中數(shù)據(jù)頁的修改、Undo Log、Redo Log緩存記錄,以及記錄Binlog cache緩存。
  • commit階段,這個階段是將Redo Log中事務(wù)狀態(tài)標(biāo)記為commit。
  • 此時Binlog和Redo Log都已經(jīng)寫入磁盤,如果觸發(fā)了刷新臟頁的操作,先把臟頁copy到double write buffer里,double write buffer 的內(nèi)存數(shù)據(jù)刷到磁盤中的共享表空間 ibdata,再刷到數(shù)據(jù)磁盤上數(shù)據(jù)文件 ibd。

以上就是修改語句的全部流程,可以看到,上圖中涉及刷盤的操作本初沒有詳細(xì)講解,接下來,將結(jié)合三大日志,詳細(xì)講解刷盤相關(guān)操作。

7.Undo Log、Redo Log和Binlog日志實(shí)現(xiàn)事務(wù)的ACID特性

7.1 事務(wù)的ACID特性概述

在深入理解Undo Log、Redo Log和Binlog之前,首先需要明確事務(wù)的ACID特性,這些特性是確保數(shù)據(jù)庫操作可靠性和一致性的基石。

  • 原子性(Atomicity):事務(wù)中的所有操作要么全部成功,要么全部失敗,不會出現(xiàn)部分完成的狀態(tài)。
  • 一致性(Consistency):事務(wù)的執(zhí)行必須使數(shù)據(jù)庫從一個一致性狀態(tài)轉(zhuǎn)變到另一個一致性狀態(tài),確保數(shù)據(jù)的完整性。
  • 隔離性(Isolation):指在多事務(wù)并發(fā)執(zhí)行時,一個事務(wù)的操作對其他事務(wù)的影響程度。它確保事務(wù)之間的操作是相互獨(dú)立的,避免并發(fā)帶來的數(shù)據(jù)不一致問題。
  • 持久性(Durability):一旦事務(wù)提交,其對數(shù)據(jù)庫的修改是永久性的,即使系統(tǒng)發(fā)生故障也不會丟失。

7.2 Undo Log(回滾日志)

Undo Log記錄了事務(wù)在修改數(shù)據(jù)之前的原始狀態(tài),用于在事務(wù)回滾時撤銷未完成的修改,確保事務(wù)的原子性和隔離性。

Undo Log主要的功能有兩個:事務(wù)回滾和MVCC。

首先來說事務(wù)回滾,事務(wù)如何通過Undo Log進(jìn)行回滾操作呢?其實(shí)很簡單,只需要在Undo Log日志中記錄事務(wù)中的反向操作即可,發(fā)生回滾時直接通過Undo Log中記錄的反向操作進(jìn)行恢復(fù)。例如:

  • 事務(wù)進(jìn)行insert操作,Undo Log記錄delete操作。
  • 事務(wù)進(jìn)行delete操作,Undo Log記錄insert操作。
  • 事務(wù)進(jìn)行update操作(value1 改為value2 ),Undo Log記錄update操作(value2 改為value1 )。

Undo Log 保存的是一個版本的鏈路,使用roll_pointer這個字段來連接的。多個事務(wù)的Undo Log 日志組成了一個版本鏈,如圖:

圖片圖片

在上圖中:

  • trx_id代表事務(wù)id,記錄了這一系列事務(wù)操作是基于哪個事務(wù)。
  • roll_pointer代表回滾指針,就是當(dāng)要發(fā)生rollback回滾操作時,就通過roll_pointer進(jìn)行回滾,這個鏈表稱為版本鏈。在事務(wù)執(zhí)行過程中,如果發(fā)生錯誤或主動回滾,Undo Log 會將數(shù)據(jù)恢復(fù)到事務(wù)開始前的狀態(tài),確保事務(wù)的所有操作要么全部完成,要么全部撤銷,從而實(shí)現(xiàn)原子性。

為了 提升 Undo Log 讀寫性能, Undo頁還存在于Buffer Pool中,因為Buffer Pool 是 InnoDB 的內(nèi)存緩存,用于存儲數(shù)據(jù)頁和索引頁,以便快速訪問。它通過減少磁盤 I/O,提高數(shù)據(jù)庫的整體性能。將Undo頁放在緩存中,可以加速事務(wù)的回滾和數(shù)據(jù)恢復(fù)。

當(dāng)事務(wù)commit之后,不會立即刪除,會保留至所有快照讀完成。后續(xù)會通過后臺線程中的Master Thread或Purge Thread進(jìn)行Undo Page的回收工作。

再說MVCC,實(shí)現(xiàn)了自己 Copy-On-Write思想提升并發(fā)能力的時候, 也需要數(shù)據(jù)的副本,如上圖,既然已經(jīng)存在了這么多Undo Log的副本,那么MVCC可以直接復(fù)用這些副本數(shù)據(jù)。

所以,Undo Log中的副本,可以用于實(shí)現(xiàn)多版本并發(fā)控制(MVCC),提升事務(wù)的并發(fā)性能,同時每一個事務(wù)操作自己的副本,實(shí)現(xiàn)事務(wù)的隔離性。

實(shí)現(xiàn)MVCC主要通過三個元素,一個是我們上面已經(jīng)提到的Undo Log版本鏈,一個是readView,最后就是我們上面已經(jīng)提到的這些字段。因為整個課題比較大,在這里就不在過多的贅述。

7.3 Redo Log(重做日志)

Redo Log記錄了事務(wù)對數(shù)據(jù)的修改操作,用于在系統(tǒng)崩潰后恢復(fù)已提交事務(wù)的修改,確保事務(wù)的持久性。

7.3.1 Redo Log 日志執(zhí)行過程

當(dāng)一個更新事務(wù)到達(dá)時,Redo Log的處理過程如下:

  • 首先,從磁盤讀取原始數(shù)據(jù)到Buffer Pool(內(nèi)存中),然后在內(nèi)存中對數(shù)據(jù)進(jìn)行修改,修改后的數(shù)據(jù)將被標(biāo)記為臟頁。
  • 接著,系統(tǒng)會生成一系列重做日志,并將其寫入Redo Log Buffer,日志內(nèi)容記錄的是數(shù)據(jù)修改后的新值。
  • 當(dāng)事務(wù)提交(commit)時,Redo Log Buffer中的內(nèi)容會被刷新到Redo Log File中,并采用追加寫的方式將日志記錄寫入文件。
  • 定期會將內(nèi)存中修改過的數(shù)據(jù)刷新回磁盤,以保證數(shù)據(jù)持久性。

Redo Log利用WAL(Write-Ahead Logging)機(jī)制來保證故障恢復(fù)的安全性(crash-safe)。

WAL的核心思想是先寫日志,再寫磁盤。根本原因是機(jī)械磁盤的性能,日志是順序?qū)懀鴶?shù)據(jù)頁是隨機(jī)寫。順序?qū)懙男阅芨?,所以先把日志歸檔。具體來說,當(dāng)緩存頁被修改(即變成臟頁)后,相關(guān)的操作會先記錄到Redo Log Buffer中。在事務(wù)提交(commit)時,后臺線程會將Redo Log Buffer中的內(nèi)容刷新到磁盤上(事務(wù)提交是Redo Log默認(rèn)刷盤的時機(jī))。此時,雖然臟頁還沒有寫回磁盤,但只要Redo Log成功寫入磁盤,就可以認(rèn)為此次修改操作已完成。這是因為,即使發(fā)生故障導(dǎo)致臟頁丟失,我們也可以通過磁盤上的Redo Log來恢復(fù)數(shù)據(jù)。因此,Redo Log與Undo Log的配合作用如下:

  • 若事務(wù)在提交前發(fā)生崩潰,則可以通過Undo Log回滾事務(wù)。
  • 若事務(wù)在提交后發(fā)生崩潰,則可以通過Redo Log來恢復(fù)事務(wù)。

7.3.2 Redo Log日志文件寫入機(jī)制

Redo Log采用固定大小并循環(huán)寫入的方式,類似環(huán)形緩沖區(qū)。當(dāng)日志文件寫滿時,會從頭開始覆蓋之前的內(nèi)容。這樣的設(shè)計是因為Redo Log記錄的是數(shù)據(jù)頁的修改,而一旦Buffer Pool中的數(shù)據(jù)頁被刷寫到磁盤,之前的Redo Log記錄就不在有效。新的日志會覆蓋這些過時的記錄。此外,硬盤上的Redo Log文件并非單一存在,而是以文件組的形式存儲,每個文件的大小都相同。

圖片圖片

在寫入數(shù)據(jù)的同時,也需要執(zhí)行擦除操作。Redo Log成功刷盤到磁盤后,才可以進(jìn)行擦除。因此,我們使用兩個指針來管理這一過程:

  • write pos:表示當(dāng)前日志記錄寫入的位置,即當(dāng)前Redo Log文件已寫到哪里。
  • checkpoint:表示當(dāng)前可以擦除的位置,即Redo Log文件中哪些記錄已不在需要,可以被新的日志覆蓋。

圖片圖片

在圖示中,黃色部分表示已寫入完成的區(qū)域,而綠色部分則代表空閑區(qū)域。

write pos和checkpoint指針之間的綠色區(qū)域,表示剩余的可寫入空間,即Redo Log文件的空閑/可用部分。

當(dāng)進(jìn)行數(shù)據(jù)頁刷盤操作(checkpoint)時,checkpoint指針會順時針移動,覆蓋掉已寫入的黃色區(qū)域,使其變?yōu)榫G色。

當(dāng)write pos追趕上checkpoint時,意味著Redo Log文件已滿,此時必須強(qiáng)制執(zhí)行checkpoint操作,刷新Buffer Pool中的臟頁并將其寫入磁盤。隨后,checkpoint指針會被移動,這樣就可以繼續(xù)向Redo Log文件中寫入新的數(shù)據(jù)。

7.3.3 Redo Log Buffer刷盤策略

Redo Log Buffer刷盤策略主分為三種,由核心參數(shù)innodb_flush_log_at_trx_commit來控制。具體的流程如同下圖:

圖片圖片

  • innodb_flush_log_at_trx_commit=0

刷盤時機(jī):InnoDB每秒鐘會將日志緩存(Redo Log Buffer)刷新到磁盤,而不是在每次事務(wù)提交時進(jìn)行刷新。即便事務(wù)已提交,日志僅會寫入內(nèi)存中的日志緩沖區(qū),1秒鐘后由后臺線程將其寫入磁盤。

持久性:如果發(fā)生數(shù)據(jù)庫崩潰,可能會丟失最近1秒內(nèi)的事務(wù)數(shù)據(jù)。

性能:這種方式提供最高的性能,因為磁盤寫入的頻率最小,但相應(yīng)的持久性較弱。

圖片圖片

  • innodb_flush_log_at_trx_commit=1(默認(rèn)設(shè)置)

當(dāng)設(shè)置為1時,表示每次事務(wù)提交時,都會執(zhí)行刷盤操作。這意味著在默認(rèn)配置下,系統(tǒng)提供高可靠性,但性能較低。

刷盤時機(jī):每當(dāng)事務(wù)提交時,InnoDB會將日志緩沖區(qū)的內(nèi)容寫入到文件系統(tǒng)緩存中,并立即使用fsync將其刷新到磁盤。

持久性:這種設(shè)置提供了最高級別的持久性,確保每次事務(wù)提交后日志已持久化到磁盤。如果發(fā)生崩潰,最多會丟失尚未提交的事務(wù)。

性能:性能較差,因為每次事務(wù)提交都需要進(jìn)行磁盤寫入操作。在高并發(fā)寫入的環(huán)境中,頻繁的磁盤I/O可能會成為系統(tǒng)的性能瓶頸。

  • innodb_flush_log_at_trx_commit=2

當(dāng)設(shè)置為2時,表示每次事務(wù)提交時,InnoDB僅將Redo Log緩沖區(qū)的內(nèi)容寫入操作系統(tǒng)的文件系統(tǒng)緩存(Page Cache),而實(shí)際的磁盤刷盤操作由操作系統(tǒng)來負(fù)責(zé)。

刷盤時機(jī):在每次事務(wù)提交時,InnoDB會將日志緩沖區(qū)的內(nèi)容寫入操作系統(tǒng)的文件系統(tǒng)緩存(Page Cache),此時并不會執(zhí)行fsync操作,日志的實(shí)際寫入磁盤由操作系統(tǒng)決定。

持久性:如果數(shù)據(jù)庫發(fā)生崩潰,但服務(wù)器沒有崩潰,數(shù)據(jù)不會丟失。如果服務(wù)器也發(fā)生崩潰,Page Cache默認(rèn)保留最近5秒的數(shù)據(jù),最多丟失最近5秒內(nèi)的事務(wù)。但與innodb_flush_log_at_trx_commit=0不同,日志至少會寫入文件系統(tǒng)緩存,這為數(shù)據(jù)安全性提供了一定保障。

性能:性能較高,因為每次事務(wù)提交時,只需將日志寫入操作系統(tǒng)的內(nèi)存緩存,而不需要立即執(zhí)行磁盤I/O操作,從而減少了磁盤操作的頻率。

以上三種策略中:

  • 如果是對數(shù)據(jù)安全性要求比較高的場景,則需要將參數(shù)設(shè)置為1,因為1的安全性最高。
  • 如果是在一些可以容忍數(shù)據(jù)庫崩潰時丟失 1s 數(shù)據(jù)的場景,我們可以將該值設(shè)置為 0,這樣可以明顯地減少日志同步到磁盤的 I/O 操作。
  • 如果是需要安全性和性能折中的方案,可以將參數(shù)設(shè)置為2,雖然參數(shù) 2 沒有參數(shù) 0 的性能高,但是數(shù)據(jù)安全性方面比參數(shù) 0 強(qiáng),因為參數(shù) 2 只要操作系統(tǒng)不宕機(jī),即使數(shù)據(jù)庫崩潰了,也不會丟失數(shù)據(jù),同時性能方便比參數(shù) 1 高。

7.4 Binlog(二進(jìn)制日志)

Binlog是MySQL特有的一種日志機(jī)制,記錄了所有導(dǎo)致數(shù)據(jù)庫狀態(tài)變化的操作(如INSERT、UPDATE、DELETE)。Binlog主要有兩個功能:

  • 備份恢復(fù),實(shí)現(xiàn)崩潰一致性(Crash Consistency)。原理是每次事務(wù)進(jìn)行提交時,都會將增、刪、改操作以追加的方式記錄到Binlog文件中。
  • 主從復(fù)制,實(shí)現(xiàn) 主從復(fù)制的一致性。原理是主從復(fù)制常用于MySQL主從集群搭建,MySQL從節(jié)點(diǎn)通過監(jiān)聽主節(jié)點(diǎn)Binlog日志進(jìn)行同步即可。

7.4.1 Binlog的刷盤時機(jī)

事務(wù)執(zhí)行過程中,先把日志寫到 Binlog Cache(Server 層的 Cache),事務(wù)提交的時候,再把 Binlog Cache 寫到 Binlog 文件中。由于一個事務(wù)的Binlog日志必須作為一個整體寫入,不能拆分,不論事務(wù)多大,都會確保日志在一次操作中完整寫入。因此,系統(tǒng)為每個線程分配了一個塊內(nèi)存用于存儲Binlog Cache。盡管每個線程有自己的Binlog Cache,最終這些日志都會寫入到同一個Binlog文件中。

圖片圖片

  • 圖中的 write,指的就是指把日志寫入到 Page Cache ,但是并沒有把數(shù)據(jù)持久化到磁盤,因為數(shù)據(jù)還緩存在文件系統(tǒng)的 Page Cache 里,write 的寫入速度還是比較快的,因為不涉及磁盤 I/O。
  • 圖中的 fsync,才是將數(shù)據(jù)持久化到磁盤的操作,這里就會涉及磁盤 I/O,所以頻繁的 fsync 會導(dǎo)致磁盤的 I/O 升高。

MySQL提供了一個sync_binlog參數(shù),用于控制Binlog日志寫入磁盤的頻率:

  • sync_binlog = 0:每次事務(wù)提交時,只進(jìn)行寫操作(write),不執(zhí)行fsync,具體何時將數(shù)據(jù)持久化到磁盤由操作系統(tǒng)決定。
  • sync_binlog = 1:每次事務(wù)提交時,先進(jìn)行寫操作(write),然后立即執(zhí)行fsync,確保日志被持久化到磁盤。
  • sync_binlog = N(N > 1):每次事務(wù)提交時只執(zhí)行寫操作(write),但累積N個事務(wù)后才會執(zhí)行fsync,將日志持久化到磁盤。在MySQL中,默認(rèn)的sync_binlog設(shè)置為0,意味著沒有強(qiáng)制性的磁盤刷新操作,這樣可以獲得最佳的性能,但也伴隨較高的風(fēng)險。如果操作系統(tǒng)發(fā)生異常重啟,尚未持久化到磁盤的Binlog數(shù)據(jù)將會丟失。

當(dāng)sync_binlog設(shè)置為1時,系統(tǒng)提供最強(qiáng)的安全性,確保即使發(fā)生異常重啟,也最多丟失一個事務(wù)的Binlog,而已經(jīng)持久化的數(shù)據(jù)不會受到影響。然而,這種設(shè)置對性能的影響非常大。

如果能夠接受少量事務(wù)Binlog丟失的風(fēng)險,并希望提高寫入性能,一般可以將sync_binlog設(shè)置為100到1000之間的某個值,從而在性能和安全性之間找到平衡。

8 總結(jié)

通過本文的深入解析,我們?nèi)媪私饬薓ySQL中Undo Log、Redo Log和Binlog三大日志機(jī)制,以及它們在保障事務(wù)ACID特性中的關(guān)鍵作用。

  • Undo Log:通過記錄數(shù)據(jù)修改前的狀態(tài),確保事務(wù)在發(fā)生錯誤或回滾時能夠恢復(fù)到初始狀態(tài),保障了原子性和隔離性。
  • Redo Log:通過記錄事務(wù)的修改操作,確保即使在系統(tǒng)崩潰后,已提交的事務(wù)也能被恢復(fù),保障了持久性。
  • Binlog:作為MySQL特有的二進(jìn)制日志,支持?jǐn)?shù)據(jù)復(fù)制和恢復(fù),進(jìn)一步增強(qiáng)了數(shù)據(jù)的持久性和系統(tǒng)的可擴(kuò)展性。理解和掌握這些日志機(jī)制,不僅有助于優(yōu)化數(shù)據(jù)庫性能,提升事務(wù)處理效率,還能在面對系統(tǒng)故障時快速進(jìn)行數(shù)據(jù)恢復(fù),確保業(yè)務(wù)的連續(xù)性和數(shù)據(jù)的安全性。同時,這些知識也為構(gòu)建高可用、高性能的數(shù)據(jù)庫架構(gòu)提供了堅實(shí)的理論基礎(chǔ)。

在實(shí)際應(yīng)用中,合理配置和管理這些日志機(jī)制,結(jié)合具體業(yè)務(wù)需求進(jìn)行優(yōu)化,是每位數(shù)據(jù)庫管理員和開發(fā)者需要掌握的重要技能。希望通過本文,您能夠?qū)ySQL的核心機(jī)制有更深刻的理解,并在實(shí)際工作中靈活運(yùn)用,為構(gòu)建穩(wěn)定可靠的數(shù)據(jù)庫系統(tǒng)貢獻(xiàn)力量。

關(guān)于作者:朱洪旭 俠客匯Java開發(fā)工程師

參考資料

[1] 《MySQL實(shí)戰(zhàn)45講》

[2] https://zhuanlan.zhihu.com/p/451007506

[3] https://zhuanlan.zhihu.com/p/667283776

責(zé)任編輯:武曉燕 來源: 轉(zhuǎn)轉(zhuǎn)技術(shù)
相關(guān)推薦

2025-01-15 13:19:09

MySQL日志事務(wù)

2020-08-20 12:10:42

MySQL日志數(shù)據(jù)庫

2024-05-28 00:10:00

JavaMySQL數(shù)據(jù)庫

2024-05-30 08:03:17

2024-06-11 00:00:02

MySQL數(shù)據(jù)庫系統(tǒng)

2023-11-23 13:17:39

MySQL?數(shù)據(jù)庫

2021-01-26 13:47:08

MySQL存儲數(shù)據(jù)

2024-03-14 14:18:58

MySQL業(yè)務(wù)設(shè)計事務(wù)

2025-01-20 08:20:00

redo logMySQL數(shù)據(jù)庫

2021-02-09 10:07:23

面試MySQL存儲

2021-07-28 08:32:03

MySQLRedo存儲

2020-11-11 07:32:18

MySQL InnoDB 存儲

2021-10-04 09:23:30

Redo日志內(nèi)存

2021-05-28 11:18:50

MySQLbin logredo log

2010-01-06 09:30:51

Oracle Redo

2018-07-31 16:10:51

Redo Undo數(shù)據(jù)庫數(shù)據(jù)

2022-02-15 17:51:38

Log4j漏洞網(wǎng)絡(luò)安全

2021-12-26 18:24:51

MySQL InnoDB引擎

2022-08-26 10:11:26

MySQL數(shù)據(jù)庫

2020-09-18 11:00:28

MySQLbinlogrelay-log
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號