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

美團(tuán)二面:如何解決 Bin Log 與 Redo Log 的一致性問(wèn)題

數(shù)據(jù)庫(kù) MySQL
前面我們說(shuō)過(guò),MySQL Server 層擁有的 bin log 只能用于歸檔,不足以實(shí)現(xiàn)崩潰恢復(fù)(crash-safe),需要借助 InnoDB 引擎的 redo log 才能擁有崩潰恢復(fù)的能力。

[[442100]]

剛看見(jiàn)這個(gè)題目的時(shí)候還是有點(diǎn)懵逼的,后來(lái)才反應(yīng)過(guò)來(lái)其實(shí)問(wèn)的就是 redo log 的兩階段提交

老規(guī)矩,背誦版在文末。點(diǎn)擊閱讀原文可以直達(dá)我收錄整理的各大廠面試真題

為什么說(shuō) redo log 具有崩潰恢復(fù)的能力

前面我們說(shuō)過(guò),MySQL Server 層擁有的 bin log 只能用于歸檔,不足以實(shí)現(xiàn)崩潰恢復(fù)(crash-safe),需要借助 InnoDB 引擎的 redo log 才能擁有崩潰恢復(fù)的能力。所謂崩潰恢復(fù)就是:即使在數(shù)據(jù)庫(kù)宕機(jī)的情況下,也不會(huì)出現(xiàn)操作一半的情況

至于為什么說(shuō) redo log 具有崩潰恢復(fù)的能力,而 bin log 沒(méi)有,我們先來(lái)簡(jiǎn)單看一下這兩種日志有哪些不同點(diǎn):

1)適用對(duì)象不同:

bin log 是 MySQL 的 Server 層實(shí)現(xiàn)的,所有引擎都可以使用

而 redo log 是 InnoDB 引擎特有的

2)寫入內(nèi)容不同:

bin log 是邏輯日志,記錄的是這個(gè)語(yǔ)句的原始邏輯,比如 “給 id = 1 這一行的 age 字段加 1”

redo log 是物理日志,記錄的是 “在某個(gè)數(shù)據(jù)頁(yè)上做了什么修改”

3)寫入方式不同:

bin log 是可以追加寫入的。“追加寫” 是指 bin log 文件寫到一定大小后會(huì)切換到下一個(gè),并不會(huì)覆蓋以前的日志

redo log 是循環(huán)寫的,空間固定會(huì)被用完

可以看到,redo log 和 bin log 的一個(gè)很大的區(qū)別就是,一個(gè)是循環(huán)寫,一個(gè)是追加寫。也就是說(shuō) redo log 只會(huì)記錄未刷入磁盤的日志,已經(jīng)刷入磁盤的數(shù)據(jù)都會(huì)從 redo log 這個(gè)有限大小的日志文件里刪除。

而 bin log 是追加日志,保存的是全量的日志。這就會(huì)導(dǎo)致一個(gè)問(wèn)題,那就是沒(méi)有標(biāo)志能讓 InnoDB 從 bin log 中判斷哪些數(shù)據(jù)已經(jīng)刷入磁盤了,哪些數(shù)據(jù)還沒(méi)有。

舉個(gè)例子,bin log 記錄了兩條日志:

  1. 記錄 1:給 id = 1 這一行的 age 字段加 1 
  2. 記錄 2:給 id = 1 這一行的 age 字段加 1 

假設(shè)在記錄 1 刷盤后,記錄 2 未刷盤時(shí),數(shù)據(jù)庫(kù)崩潰。重啟后,只通過(guò) bin log 數(shù)據(jù)庫(kù)是無(wú)法判斷這兩條記錄哪條已經(jīng)寫入磁盤,哪條沒(méi)有寫入磁盤,不管是兩條都恢復(fù)至內(nèi)存,還是都不恢復(fù),對(duì) id = 1 這行數(shù)據(jù)來(lái)說(shuō),都是不對(duì)的。

但 redo log 不一樣,只要刷入磁盤的數(shù)據(jù),都會(huì)從 redo log 中被抹掉,數(shù)據(jù)庫(kù)重啟后,直接把 redo log 中的數(shù)據(jù)都恢復(fù)至內(nèi)存就可以了。

這就是為什么說(shuō) redo log 具有崩潰恢復(fù)的能力,而 bin log 不具備。

redo log 兩階段提交

前面我們介紹過(guò)一條 SQL 查詢語(yǔ)句的執(zhí)行過(guò)程,簡(jiǎn)單回顧:

MySQL 客戶端與服務(wù)器間建立連接,客戶端發(fā)送一條查詢給服務(wù)器;

服務(wù)器先檢查查詢緩存,如果命中了緩存,則立刻返回存儲(chǔ)在緩存中的結(jié)果;否則進(jìn)入下一階段;

服務(wù)器端進(jìn)行 SQL 解析、預(yù)處理,生成合法的解析樹(shù);

再由優(yōu)化器生成對(duì)應(yīng)的執(zhí)行計(jì)劃;

執(zhí)行器根據(jù)優(yōu)化器生成的執(zhí)行計(jì)劃,調(diào)用相應(yīng)的存儲(chǔ)引擎的 API 來(lái)執(zhí)行,并將執(zhí)行結(jié)果返回給客戶端

對(duì)于更新語(yǔ)句來(lái)說(shuō),這套流程同樣也是要走一遍的,不同的是,更新流程還涉及兩個(gè)重要的日志模塊 bin log 和 redo log。

以下面這條簡(jiǎn)單的 SQL 語(yǔ)句為例,我們來(lái)解釋下執(zhí)行器和 InnoDB 存儲(chǔ)引擎在更新時(shí)做了哪些事情:

  1. update table set age = age + 1 where id = 1; 

執(zhí)行器:找存儲(chǔ)引擎取到 id = 1 這一行記錄

存儲(chǔ)引擎:根據(jù)主鍵索引樹(shù)找到這一行,如果 id = 1 這一行所在的數(shù)據(jù)頁(yè)本來(lái)就在內(nèi)存池(Buffer Pool)中,就直接返回給執(zhí)行器;否則,需要先從磁盤讀入內(nèi)存池,然后再返回

執(zhí)行器:拿到存儲(chǔ)引擎返回的行記錄,把 age 字段加上 1,得到一行新的記錄,然后再調(diào)用存儲(chǔ)引擎的接口寫入這行新記錄

存儲(chǔ)引擎:將這行新數(shù)據(jù)更新到內(nèi)存中,同時(shí)將這個(gè)更新操作記錄到 redo log 里面,此時(shí) redo log 處于 prepare 狀態(tài)。然后告知執(zhí)行器執(zhí)行完成了,隨時(shí)可以提交事務(wù)

注意不要把這里的提交事務(wù)和我們 sql 語(yǔ)句中的提交事務(wù) commit 命令搞混了哈,我們這里說(shuō)的提交事務(wù),指的是事務(wù)提交過(guò)程中的一個(gè)小步驟,也是最后一步。當(dāng)這個(gè)步驟執(zhí)行完成后,commit 命令就執(zhí)行成功了。

執(zhí)行器:生成這個(gè)操作的 bin log,并把 bin log 寫入磁盤

執(zhí)行器:調(diào)用存儲(chǔ)引擎的提交事務(wù)接口

存儲(chǔ)引擎:把剛剛寫入的 redo log 狀態(tài)改成提交(commit)狀態(tài),更新完成

如下圖所示:

可以看到,所謂兩階段提交,其實(shí)就是把 redo log 的寫入拆分成了兩個(gè)步驟:prepare 和 commit。

所以,為什么要這樣設(shè)計(jì)呢?這樣設(shè)計(jì)怎么就能夠?qū)崿F(xiàn)崩潰恢復(fù)呢?

根據(jù)兩階段提交,崩潰恢復(fù)時(shí)的判斷規(guī)則是這樣的:

如果 redo log 里面的事務(wù)是完整的,也就是已經(jīng)有了 commit 標(biāo)識(shí),則直接提交

如果 redo log 里面的事務(wù)處于 prepare 狀態(tài),則判斷對(duì)應(yīng)的事務(wù) binlog 是否存在并完整

  • a. 如果 binlog 存在并完整,則提交事務(wù);
  • b. 否則,回滾事務(wù)。

當(dāng)然,這樣說(shuō)小伙伴們肯定沒(méi)法理解,下面來(lái)看幾個(gè)實(shí)際的例子:

如下圖所示,假設(shè)數(shù)據(jù)庫(kù)在寫入 redo log(prepare) 階段之后、寫入 binlog 之前,發(fā)生了崩潰,此時(shí) redo log 里面的事務(wù)處于 prepare 狀態(tài),binlog 還沒(méi)寫(對(duì)應(yīng) 2b),所以崩潰的時(shí)候,這個(gè)事務(wù)會(huì)回滾。

Why?

因?yàn)?binlog 還沒(méi)有寫入,之后從庫(kù)進(jìn)行同步的時(shí)候,無(wú)法執(zhí)行這個(gè)操作,但是實(shí)際上主庫(kù)已經(jīng)完成了這個(gè)操作,所以為了主備一致,在主庫(kù)上需要回滾這個(gè)事務(wù)

并且,由于 binlog 還沒(méi)寫,所以也就不會(huì)傳到備庫(kù),從而避免主備不一致的情況。

而如果數(shù)據(jù)庫(kù)在寫入 binlog 之后,redo log 狀態(tài)修改為 commit 前發(fā)生崩潰,此時(shí) redo log 里面的事務(wù)仍然是 prepare 狀態(tài),binlog 存在并完整(對(duì)應(yīng) 2a),所以即使在這個(gè)時(shí)刻數(shù)據(jù)庫(kù)崩潰了,事務(wù)仍然會(huì)被正常提交。

Why?

因?yàn)?binlog 已經(jīng)寫入成功了,這樣之后就會(huì)被從庫(kù)同步過(guò)去,但是實(shí)際上主庫(kù)并沒(méi)有完成這個(gè)操作,所以為了主備一致,在主庫(kù)上需要提交這個(gè)事務(wù)。

所以,其實(shí)可以看出來(lái),處于 prepare 階段的 redo log 加上完整的 bin log,就能保證數(shù)據(jù)庫(kù)的崩潰恢復(fù)了。

可能有同學(xué)就會(huì)問(wèn)了,MySQL 咋知道 bin log 是不是完整的?

簡(jiǎn)單來(lái)說(shuō),一個(gè)事務(wù)的 binlog 是有完整格式的(這個(gè)我們?cè)诤竺娴奈恼轮袝?huì)詳細(xì)解釋):

  • statement 格式的 bin log,最后會(huì)有 COMMIT
  • row 格式的 bin log,最后會(huì)有 XID event

而對(duì)于 bin log 可能會(huì)在中間出錯(cuò)的情況,MySQL 5.6.2 版本以后引入了 binlog-checksum 參數(shù),用來(lái)驗(yàn)證 bin log 內(nèi)容的正確性。

思考一個(gè)問(wèn)題,兩階段提交是必要的嗎?可不可以先 redo log 寫完,再寫 bin log 或者反過(guò)來(lái)?

1)對(duì)于先寫完 redo log 后寫 bin log 的情況:

假設(shè)在 redo log 寫完,bin log 還沒(méi)有寫完的時(shí)候,MySQL 崩潰。主庫(kù)中的數(shù)據(jù)確實(shí)已經(jīng)被修改了,但是這時(shí)候 bin log 里面并沒(méi)有記錄這個(gè)語(yǔ)句。因此,從庫(kù)同步的時(shí)候,就會(huì)丟失這個(gè)更新,和主庫(kù)不一致。

2)對(duì)于先寫完 binlog 后寫 redo log 的情況:

如果在 bin log 寫完,redo log 還沒(méi)寫的時(shí)候,MySQL 崩潰。因?yàn)?binlog 已經(jīng)寫入成功了,這樣之后就會(huì)被從庫(kù)同步過(guò)去,但是實(shí)際上 redo log 還沒(méi)寫,主庫(kù)并沒(méi)有完成這個(gè)操作,所以從庫(kù)相比主庫(kù)就會(huì)多執(zhí)行一個(gè)事務(wù),導(dǎo)致主備不一致

最后放上這道題的背誦版:

面試官:

  • 問(wèn)法 1:如何解決 bin log 與 redo log 的一致性問(wèn)題?
  • 問(wèn)法 2:一條 SQL 更新語(yǔ)句是如何執(zhí)行的?
  • 問(wèn)法 3:講一下 redo log / redo log 兩階段提交原理

小牛肉:

所謂兩階段提交,其實(shí)就是把 redo log 的寫入拆分成了兩個(gè)步驟:prepare 和 commit。

首先,存儲(chǔ)引擎將執(zhí)行更新好的新數(shù)據(jù)存到內(nèi)存中,同時(shí)將這個(gè)更新操作記錄到 redo log 里面,此時(shí) redo log 處于 prepare 狀態(tài)。然后告知執(zhí)行器執(zhí)行完成了,隨時(shí)可以提交事務(wù)

然后執(zhí)行器生成這個(gè)操作的 bin log,并把 bin log 寫入磁盤

最后執(zhí)行器調(diào)用存儲(chǔ)引擎的提交事務(wù)接口,存儲(chǔ)引擎把剛剛寫入的 redo log 狀態(tài)改成提交(commit)狀態(tài),更新完成

如果數(shù)據(jù)庫(kù)在寫入 redo log(prepare) 階段之后、寫入 binlog 之前,發(fā)生了崩潰:

此時(shí) redo log 里面的事務(wù)處于 prepare 狀態(tài),binlog 還沒(méi)寫,之后從庫(kù)進(jìn)行同步的時(shí)候,無(wú)法執(zhí)行這個(gè)操作,但是實(shí)際上主庫(kù)已經(jīng)完成了這個(gè)操作,所以為了主備一致,MySQL 崩潰時(shí)會(huì)在主庫(kù)上回滾這個(gè)事務(wù)

 

而如果數(shù)據(jù)庫(kù)在寫入 binlog 之后,redo log 狀態(tài)修改為 commit 前發(fā)生崩潰,此時(shí) redo log 里面的事務(wù)仍然是 prepare 狀態(tài),binlog 存在并完整,這樣之后就會(huì)被從庫(kù)同步過(guò)去,但是實(shí)際上主庫(kù)并沒(méi)有完成這個(gè)操作,所以為了主備一致,即使在這個(gè)時(shí)刻數(shù)據(jù)庫(kù)崩潰了,主庫(kù)上事務(wù)仍然會(huì)被正常提交。

 

責(zé)任編輯:武曉燕 來(lái)源: 飛天小牛肉
相關(guān)推薦

2021-06-04 09:56:12

RedisMySQL美團(tuán)

2024-08-06 09:42:23

2024-04-11 13:45:14

Redis數(shù)據(jù)庫(kù)緩存

2019-05-27 09:00:00

蘇寧智慧零售平臺(tái)數(shù)據(jù)庫(kù)

2024-06-11 00:00:02

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

2016-11-29 09:00:19

分布式數(shù)據(jù)一致性CAS

2022-09-06 15:30:20

緩存一致性

2022-08-24 07:44:53

流量系統(tǒng)數(shù)據(jù)

2024-11-14 07:10:00

2022-08-11 07:55:05

數(shù)據(jù)庫(kù)Mysql

2021-05-28 11:18:50

MySQLbin logredo log

2020-09-04 06:32:08

緩存數(shù)據(jù)庫(kù)接口

2019-02-13 11:04:42

系統(tǒng)緩存軟件

2021-09-18 08:54:19

zookeeper一致性算法CAP

2023-08-01 07:42:33

Redis數(shù)據(jù)項(xiàng)目

2012-09-24 09:35:42

分布式系統(tǒng)

2022-06-21 21:47:13

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

2023-04-13 08:15:47

Redis緩存一致性

2021-04-18 15:01:56

緩存系統(tǒng)數(shù)據(jù)

2024-11-07 22:57:30

點(diǎn)贊
收藏

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