新聞中心
本文轉(zhuǎn)載自微信公眾號「石杉的架構(gòu)筆記」,作者崔皓。轉(zhuǎn)載本文請聯(lián)系石杉的架構(gòu)筆記公眾號。

“專業(yè)、務(wù)實(shí)、高效、創(chuàng)新、把客戶的事當(dāng)成自己的事”是我們每一個(gè)人一直以來堅(jiān)持追求的企業(yè)文化。 創(chuàng)新互聯(lián)是您可以信賴的網(wǎng)站建設(shè)服務(wù)商、專業(yè)的互聯(lián)網(wǎng)服務(wù)提供商! 專注于成都網(wǎng)站制作、成都做網(wǎng)站、軟件開發(fā)、設(shè)計(jì)服務(wù)業(yè)務(wù)。我們始終堅(jiān)持以客戶需求為導(dǎo)向,結(jié)合用戶體驗(yàn)與視覺傳達(dá),提供有針對性的項(xiàng)目解決方案,提供專業(yè)性的建議,創(chuàng)新互聯(lián)建站將不斷地超越自我,追逐市場,引領(lǐng)市場!
MySQL是常用的數(shù)據(jù)庫存儲應(yīng)用,我們利用它存儲信息、查詢信息、處理事務(wù)。特別是為了提高可用性會用到事務(wù)一致性、主從復(fù)制、數(shù)據(jù)恢復(fù)等功能。我們在使用這些功能的時(shí)候,是否想過其背后有哪些原理和機(jī)制在支撐?今天我們聚焦redo log和binlog兩個(gè)MySQL的日志機(jī)制,以及它們是如何配合提高M(jìn)ySQL存儲可靠性的。今天會學(xué)到以下內(nèi)容:
- Redo log
Redo log 解決了什么問題?
Redo log的執(zhí)行流程
Redo log的寫入方式
Redo log記錄形式
- Binlog
a.Binlog解決了什么問題?
b.Binlog的日志格式
- Redo log 與Binlog的區(qū)別與合作
Redo log
1.Redo log 解決了什么問題?
MySQL應(yīng)用中處理事務(wù)是一個(gè)重要的任務(wù),而在事務(wù)處理的四個(gè)特性中(ACID),存在一個(gè)持久性(Durability),它表示在事務(wù)執(zhí)行過程中,對數(shù)據(jù)的所有改動(dòng)都必須在事務(wù)成功結(jié)束前保存至某種物理存儲設(shè)備中。
換句話說,只要事務(wù)提交成功,那么對數(shù)據(jù)庫做的修改就被永久保存下來了,不可能因?yàn)槿魏卧蛟倩氐皆瓉淼臓顟B(tài)。那么為什么要在MySQL中考慮事務(wù)持久性的問題呢?假設(shè)這么一種場景,當(dāng)數(shù)據(jù)存儲的事務(wù)正在執(zhí)行但是數(shù)據(jù)還沒有保存的時(shí)候,數(shù)據(jù)庫宕機(jī)了,那么這些沒來得及存儲到磁盤的數(shù)據(jù)就丟失了,如果此時(shí)有一種機(jī)制能夠記錄這個(gè)事務(wù)的操作,當(dāng)數(shù)據(jù)庫服務(wù)恢復(fù)的時(shí)候,運(yùn)行記錄的操作那么這些沒有來得及存儲的數(shù)據(jù)就能夠正確保存了。
Redo log 就是通過這種手段來實(shí)現(xiàn)事務(wù)持久性的。上面的場景中是數(shù)據(jù)庫服務(wù)器宕機(jī),如果發(fā)生其他故障導(dǎo)致尚有臟頁未寫入磁盤的場景,也是可以通過Redo log恢復(fù)的。
1.Redo log的執(zhí)行流程
了解了為什么使用redo log 以后再來看看其執(zhí)行流程,如圖1 所示:
圖1 redo log執(zhí)行流程
該泳道圖由MySQL客戶端、MySQL Server 層和MySQL 存儲引擎層組成,由于redo log是在Innodb存儲引擎中使用的,這里假設(shè)存儲引擎就是Innodb。由于MySQL Server 層主要負(fù)責(zé)SQL語句的分析、優(yōu)化和執(zhí)行工作,而MySQL存儲引擎層主要負(fù)責(zé)存儲工作,redo log 也運(yùn)行在這一層。
跟隨圖中的序號來看看redo log 的運(yùn)行流程。
- 1. 從MySQL客戶端請求語句“update T set a=1 where id=2”,并發(fā)現(xiàn)MySQL Server 層。
- 2. 接收到SQL請求以后MySQL Server 層會對其進(jìn)行分析、優(yōu)化、執(zhí)行等處理工作,將生成的SQL執(zhí)行計(jì)劃發(fā)到存儲引擎層執(zhí)行。
- 3. 存儲引擎層將“a修改為1”的這個(gè)操作記錄到內(nèi)存中。
- 4. 記錄到內(nèi)存以后會修改redo log 的記錄,會在添加一行記錄,其內(nèi)容是“需要在哪個(gè)數(shù)據(jù)頁上做什么修改”。
- 5. 此后,將事務(wù)的狀態(tài)設(shè)置為prepare ,說明已經(jīng)準(zhǔn)備好提交事務(wù)了。
- 6. 等到MySQL Server 層處理完事務(wù)以后,會將事務(wù)的狀態(tài)設(shè)置為commit,也就是提交該事務(wù)。
- 7. 在收到事務(wù)提交的請求以后,redo log 會把剛才寫入內(nèi)存中的操作記錄寫入到磁盤中,從而完成整個(gè)日志的記錄過程。
2Redo log的寫入方式
從上面介紹的Redo log 的執(zhí)行流程中不難看出,redo log在寫入磁盤之前會先將內(nèi)容寫到內(nèi)存中。因此,redo log的寫入包括兩部分內(nèi)容:一部分是內(nèi)存中的日志緩沖,稱作redo log buffer;另一部分是磁盤日志文件,稱作 redo log file。MySQL每執(zhí)行一條DML語句,先將更新記錄寫入redo log buffer ,然后再寫入redo log file。我們將這種先寫日志,再寫磁盤的方式稱為 WAL(Write-Ahead Logging)技術(shù)。
如圖2所示:
圖2 redo log 寫入方式
順著箭頭的方向從左往右看,日志最開始會寫入位于存儲引擎Innodb的redo log buffer中,這個(gè)也就是所謂的用戶空間(user space),然后再將日志保存到操作系統(tǒng)內(nèi)核空間(kernel space)的緩沖區(qū)(OS buffer)中。
最后,再從OS buffer寫入到磁盤上的redo log file中,完成寫入操作,這個(gè)寫入磁盤的操作也稱作“刷盤”。
了解了redo log的寫入方式之后,我們發(fā)現(xiàn)主要完成的操作是redo log buffer 到磁盤的redo log file的寫入過程,其中需要經(jīng)過OS buffer進(jìn)行中轉(zhuǎn)。關(guān)于redo log buffer寫入redo log file的時(shí)機(jī),可以通過 參數(shù)innodb_flush_log_at_trx_commit 進(jìn)行配置,各參數(shù)值含義如下:
參數(shù)為0的時(shí)候,稱為“延遲寫”。事務(wù)提交時(shí)不會將redo log buffer中日志寫入到OS buffer,而是每秒寫入OS buffer并調(diào)用寫入到redo log file中。換句話說,這種方式每秒會發(fā)起寫入磁盤的操作,假設(shè)系統(tǒng)崩潰,只會丟失1秒鐘的數(shù)據(jù)。
參數(shù)為1 的時(shí)候,稱為“實(shí)時(shí)寫,實(shí)時(shí)刷”。事務(wù)每次提交都會將redo log buffer中的日志寫入OS buffer并保存到redo log file中。其有點(diǎn)是,即使系統(tǒng)崩潰也不會丟失任何數(shù)據(jù),缺點(diǎn)也很明顯就是每次事務(wù)提交都要進(jìn)行磁盤操作,性能較差。
參數(shù)為2的時(shí)候,稱為“實(shí)時(shí)寫,延遲刷”。每次事務(wù)提交寫入到OS buffer,然后是每秒將日志寫入到redo log file。這樣性能會好點(diǎn),缺點(diǎn)是在系統(tǒng)崩潰的時(shí)候會丟失1秒中的事務(wù)數(shù)據(jù)。
3.Redo log記錄形式
redo log是通過循環(huán)寫入的方式保存的。
如圖3所示:
圖3 redo log 循環(huán)寫入(素材來源于互聯(lián)網(wǎng))
redo log buffer(內(nèi)存中)是由首尾相連的四個(gè)文件組成的,它們分別是:ib_logfile_1、ib_logfile_2、ib_logfile_3、ib_logfile_4。
寫入的方式也是從文件的頭部開始寫入(假設(shè)),每增加一條日志記錄就往文件的尾部添加,直到把四個(gè)文件寫滿,再回到文件開頭的地方(ib_logfile_1)繼續(xù)寫,繼續(xù)寫的時(shí)候會覆蓋之前的記錄。
圖3中write pos表示當(dāng)前寫入記錄位置(寫入磁盤的數(shù)據(jù)頁的邏輯序列位置),check point表示刷盤(寫入磁盤)后對應(yīng)的位置。write pos到check point之間的部分用來記錄新日志,也就是留給新記錄的空間。check point到write pos之間是待刷盤的記錄,如果不刷盤會被新記錄覆蓋。
當(dāng)write pos指針追上check point的時(shí)候(也就是新記錄即將覆蓋老記錄的時(shí)刻),會推動(dòng)check point向前移動(dòng),也就是催促其將記錄刷到磁盤中,這樣好空出位置給新記錄。
當(dāng)redo log buffer根據(jù)check pint刷盤以后,針對Innodb引擎而言是以頁為單位進(jìn)行磁盤存儲,一個(gè)事務(wù)可能一個(gè)或者多個(gè)數(shù)據(jù)頁,每個(gè)頁面修改多個(gè)字節(jié)。當(dāng)重新啟動(dòng)Innodb存儲引擎的時(shí)候,是會進(jìn)行恢復(fù)操作。因?yàn)閞edo log記錄的是數(shù)據(jù)頁的物理變化,恢復(fù)的速度比邏輯日志(binlog)要快。
在重啟Innodb時(shí),首先會檢查磁盤中數(shù)據(jù)頁的邏輯序列位置,如果數(shù)據(jù)頁的邏輯序列位置小于日志中的位置,則會從check point開始恢復(fù)。如果宕機(jī)的時(shí)候,正處于check point的刷盤過程中,且數(shù)據(jù)頁的刷盤進(jìn)度超過了日志頁的刷盤進(jìn)度,此時(shí)會出現(xiàn)數(shù)據(jù)頁中記錄的邏輯序列位置大于日志中的邏輯序列位置,這時(shí)超出日志進(jìn)度的部分將不會重做,因?yàn)檫@本身就表示已經(jīng)做過的事情,無需再重做。
Binlog
4.Binlog 解決了什么問題?
對于MySQL數(shù)據(jù)庫而言增加數(shù)據(jù)的可靠性是一個(gè)永恒的話題,其中主從復(fù)制和數(shù)據(jù)恢復(fù)就是增強(qiáng)數(shù)據(jù)可靠性的兩個(gè)重要功能。Binlog就是為實(shí)現(xiàn)這兩個(gè)功能而設(shè)置的。
主從復(fù)制的場景中在Master 端會開啟binlog ,然后將 binlog 發(fā)送到各個(gè)Slave 端,Slave 端重放binlog 從而達(dá)到Slave 端的數(shù)據(jù)和Master端的數(shù)據(jù)保持一致。在數(shù)據(jù)恢復(fù)場景,通過使用mysqlbinlog 工具以及對應(yīng)的binlog 將數(shù)據(jù)恢復(fù)到指定的時(shí)間點(diǎn)。那么可以把binlog 解決的問題總結(jié)為兩點(diǎn),就是主從復(fù)制和數(shù)據(jù)恢復(fù)。
5.Binlog的日志格式
從記錄方式上來看binlog通過追加的方式記錄,當(dāng)日志文件尺寸大于給定值后,后續(xù)的日志會記錄到新的文件上。這個(gè)與 redo log 的循環(huán)記錄產(chǎn)生鮮明的對比,同時(shí)binlog 可通過配置參數(shù) max_binlog_size 設(shè)置每個(gè)binlog 文件的大小。
從日志格式來看,Binlog 日志有三種格式,分別為 STATMENT 、 ROW 和 MIXED 。
在 MySQL 5.7.7 之前,默認(rèn)的格式是 STATEMENT , MySQL 5.7.7 之后,默認(rèn)值是 ROW 。日志格式通過 binlog-format 指定。三種格式的定義和優(yōu)缺點(diǎn)如下:
lSTATEMENT:基于SQL語句的復(fù)制(statement-based replication, SBR),記錄的是修改的SQL語句。
n 優(yōu)點(diǎn):由于不用記錄每行日志的更改,因此日志文件小,減少了日志量,節(jié)約了IO,提高了性能;
n 缺點(diǎn):準(zhǔn)確性差,對一些系統(tǒng)行數(shù)不能準(zhǔn)確復(fù)制,例如:now()、uuid()。
lROW:基于行的復(fù)制(row-based replication, RBR),不記錄每條SQL語句的上下文信息,只記錄每行實(shí)際數(shù)據(jù)的變更 。
n 優(yōu)點(diǎn): 準(zhǔn)確性強(qiáng),能夠準(zhǔn)確復(fù)制數(shù)據(jù)的變更。
n 缺點(diǎn): 產(chǎn)生的日志文件較大,從造成較大的網(wǎng)絡(luò)IO和磁盤IO。尤其是alter table的時(shí)候會讓日志暴漲。
lMIXED:基于STATMENT和ROW兩種模式的混合復(fù)制( mixed-based replication, MBR ),默認(rèn)使用STATEMENT模式保存,STATEMENT模式無法復(fù)制的操作使用ROW模式。
n 優(yōu)點(diǎn):準(zhǔn)確性強(qiáng)、文件大小適中。
n 缺點(diǎn):有可能發(fā)生主從不一致的現(xiàn)象。
在MySQL中可以通過“show binlog events” 命令查看binlog日志的事件。如代碼段1 所示,這里通過上述命令查看“mysql-bin.000002”文件中的binlog 日志情況。
- mysql> show binlog events in 'mysql-bin.000002';
代碼段1
如圖4所示:
圖4 顯示binlog 日志內(nèi)容
通過上述命令展示對應(yīng)binlog 日志事件,從左到右展示如下:
- Log_name:描述存放binlog日志的文件名字。
- Pos:描述記日志的開始位置。
- Event_type:描述時(shí)間類型,例如:查詢、插入等。
- Server_id:對應(yīng)數(shù)據(jù)庫服務(wù)器的ID。
- End_log_pos:日志結(jié)束的位置。
- Info:執(zhí)行的SQL語句。
上面是查看日志的事件,這里也可以通過mysqlbinlog命令可以查看binlog的內(nèi)容。如代碼段2 所示,通過mysqlbinlog 命令查看mysql-bin.000002的內(nèi)容。
- mysql> mysqlbinlog 'mysql-bin.000002';
代碼段2
如圖5所示:
圖5 binlog 日志的內(nèi)容
我們將上述查看命令返回的結(jié)果截取其中一部分給大家講解,我們從上往下看:
- “at 294”說明“事件”的起點(diǎn),也就是從文件的第294字節(jié)開始。
- “120330 17:54:46”表示事件發(fā)生的時(shí)間戳信息。
- “end_log_pos 388 ”表示日志記錄結(jié)束的字節(jié)位置,也就是在文件的第388 字節(jié)截止。
- "exec_time=28",表示事件執(zhí)行花費(fèi)的時(shí)間。
- “error_code=0”,表示錯(cuò)誤代碼為0,也就是沒有錯(cuò)誤。
- “server id 1”,表示服務(wù)器的標(biāo)識id。
需要注意的是binlog的事務(wù)提交,是一次性將事務(wù)進(jìn)行提交(一個(gè)事物包含一個(gè)或者多個(gè)SQL語句)。而redo log可以在事務(wù)開始之后就開始逐步寫入磁盤。因此對于事務(wù)的提交,即便是較大的事務(wù),提交(commit)都是很快的,但是在開啟了binlog的情況下,對于較大事務(wù)的提交,可能會變得比較慢。因?yàn)閎inlog事務(wù)提交是一次性寫入。
6.Redo log與Binlog區(qū)別與合作
前面介紹了redo log 和 binlog,那么這里總結(jié)一下它們之間的區(qū)別如下表格。
| Redo log | Binlog | |
| 適用場景 | 適用于崩潰恢復(fù)(crash-safe)。 | 適用于主從復(fù)制和數(shù)據(jù)恢復(fù)。 |
| 實(shí)現(xiàn)方式 | InnoDB 引擎層實(shí)現(xiàn)的,并不是所有引擎都有。 | Server 層實(shí)現(xiàn)的,所有引擎都可以使用 binlog 日志。 |
| 記錄方式 | redo log 采用循環(huán)寫的方式記錄,當(dāng)寫到結(jié)尾時(shí),會回到開頭循環(huán)寫日志。 | Binlog通過追加的方式記錄,當(dāng)文件尺寸大于給配置值后,后續(xù)的日志會記錄到新的文件上。 |
| 文件大小 | redo log 的大小是固定的。 | Binl og 可通過配置參數(shù) max_binlog_size 設(shè)置每個(gè)binlog 文件的大小。 |
由 binlog 和 redo log 的區(qū)別可知:binlog 日志只用于歸檔,但僅僅依靠 binlog 是沒有 crash-safe 能力的。但只有 redo log 也不行,因?yàn)?redo log 是 InnoDB 特有的,且日志記錄落盤后會被覆蓋掉。因此需要 binlog 和 redo log 二者同時(shí)記錄,才能保證當(dāng)數(shù)據(jù)庫發(fā)生宕機(jī)重啟時(shí),數(shù)據(jù)不會丟失。
那么如何讓兩個(gè)日志保持一致呢?
如圖5所示:
圖5 redo log 和 binlog 事務(wù)保持一致
該圖沿用了圖1 的例子,稍微不同的是加入了一個(gè)步驟??吹骄G色虛線框的部分加入了寫入binlog的步驟。當(dāng)事務(wù)為prepare狀態(tài)的時(shí)候,在commit事務(wù)之間,會先將日志保存到binlog當(dāng)中,然后再提交給 Innodb中的redo log,最后完成commit的操作。
再聚焦于redo log 和 binlog 在提交成功和失敗兩種情況中的狀態(tài)變化。
如圖6 所示:
圖6 redo log 和 binlog 狀態(tài)變遷圖(素材來源于互聯(lián)網(wǎng))
從上往下看,先看紅色線條的部分,當(dāng)寫入redo log并且事務(wù)狀態(tài)為prepare的時(shí)候,如果寫入成功直接寫入binlog,如果binlog 寫入也成功,redo log 狀態(tài)設(shè)置為commit。如果寫入binlog的時(shí)候失敗了,沿著紅色箭頭向上回滾此次事務(wù)。再回到最上面,看綠色箭頭的部分,如果寫入redo log 狀態(tài)為prepare 此時(shí)寫入失敗,不再寫入binlog,事務(wù)直接回滾。
可以看出這里為了保持兩個(gè)日志的一致性,使用了兩段提交。redo log和binlog是兩個(gè)獨(dú)立的邏輯,如果不用兩階段提交,要么就是先寫完redo log再寫 binlog,或者先寫binlog再寫 redo log??纯催@兩種方式會有什么問題:
- 先寫redo log后寫binlog。假設(shè)在redo log寫完,binlog還沒有寫完的時(shí)候,也就是說binlog 沒有事務(wù)中更新的語句。此時(shí)MySQL重啟并使用binlog來恢復(fù)數(shù)據(jù),由于之前更新的語句沒有保存數(shù)據(jù)庫就會少了一次更新,導(dǎo)致數(shù)據(jù)的不一致。
- 先寫binlog后寫redo log。如果在binlog寫完之后服務(wù)器宕機(jī)了,由于redo log還沒寫,也就是數(shù)據(jù)還沒有寫到數(shù)據(jù)庫中。但是binlog里面已經(jīng)記錄了,意思是把本來不應(yīng)該更新的數(shù)據(jù)記錄到更新里面了。此時(shí)MySQL數(shù)據(jù)庫重啟,就會把這條不該更新的數(shù)據(jù)更新到數(shù)據(jù)庫中,導(dǎo)致數(shù)據(jù)的不一致。
總結(jié)
本文通過提高M(jìn)ySQL可靠性入手,分別介紹redo log 和binlog的實(shí)現(xiàn)機(jī)制,再合并講解兩者的區(qū)別與合作。Redo log部分首先提出redo log 的目的是解決事務(wù)提交的持久性,作為Innodb存儲引擎特有的日志模式redo log 的執(zhí)行使用了兩步提交。在寫入方式上面會先從redo log buffer 寫入到OS buffer,最后寫盤到 redo log file中。
其對應(yīng)了三種寫入配置,分別是延遲寫、實(shí)時(shí)寫實(shí)時(shí)刷和實(shí)時(shí)寫延遲刷。在記錄形式方面由四個(gè)文件循環(huán)寫入的方式進(jìn)行,通過write pos 和check point 推進(jìn)寫入的進(jìn)度。轉(zhuǎn)眼看binlog,它是為了主從復(fù)制和數(shù)據(jù)恢復(fù)而生的。有三種日志格式分別是STATEMENT、ROW、MIXED。
日志內(nèi)容主要記錄了開始的記錄點(diǎn)、結(jié)束的記錄點(diǎn)、記錄發(fā)生的時(shí)間戳、記錄花費(fèi)的時(shí)間以及具體執(zhí)行的事件操作。在介紹完兩個(gè)日志以后對其區(qū)別進(jìn)行了闡述,并且提出兩者結(jié)合可以保證數(shù)據(jù)庫宕機(jī)時(shí),數(shù)據(jù)不丟失。介紹二者在事務(wù)處理流程中是如何合作的,并且列舉了兩個(gè)場景描述了redo log 和binlog 進(jìn)行兩段提交的必要性。
新聞名稱:面試不用慌!跟著老司機(jī)吃透Redolog與Binlog
文章地址:http://m.fisionsoft.com.cn/article/cochdsp.html


咨詢
建站咨詢
