您好,登錄后才能下訂單哦!
小編給大家分享一下MySQL中常見(jiàn)的日志問(wèn)題有哪些,希望大家閱讀完這篇文章后大所收獲,下面讓我們一起去探討吧!
MySQL 里有兩個(gè)日志,即:重做日志(redo log)和歸檔日志(binlog)。
其中,binlog 可以給備庫(kù)使用,也可以保存起來(lái)用于恢復(fù)數(shù)據(jù)庫(kù)歷史數(shù)據(jù)。它是實(shí)現(xiàn)在 server 層的,所有引擎可以共用。redo log 是 InnoDB 特有的日志,用來(lái)支持 crash-safe 能力。
你一定聽(tīng)過(guò) MySQL 事務(wù)的兩階段提交,指的就是在事務(wù)提交的時(shí)候,分成 prepare 和 commit 兩個(gè)階段。
如圖 1 所示為一個(gè)事務(wù)的執(zhí)行流程,你在最后三步可以看到,redo log 先 prepare 完成,再寫 binlog,最后才進(jìn)入 redo log commit 階段。
圖 1 兩階段提交示意圖
這里,我要先和你解釋一個(gè)誤會(huì)式的問(wèn)題:這個(gè)圖不就是一個(gè) update 語(yǔ)句的執(zhí)行流程嗎,怎么還會(huì)調(diào)用 commit 語(yǔ)句?
通常情況下,你會(huì)產(chǎn)生這個(gè)疑問(wèn)的原因,在于把兩個(gè)“commit”的概念混淆了:
問(wèn)題中的“commit 語(yǔ)句”,是指 MySQL 語(yǔ)法中,用于提交一個(gè)事務(wù)的命令。一般跟 begin/start transaction 配對(duì)使用。
而我們圖中用到的這個(gè)“commit 步驟”,指的是事務(wù)提交過(guò)程中的一個(gè)小步驟,也是最后一步。當(dāng)這個(gè)步驟執(zhí)行完成后,這個(gè)事務(wù)就提交完成了。
“commit 語(yǔ)句”執(zhí)行的時(shí)候,會(huì)包含“commit 步驟”。
而我們這個(gè)例子里面,沒(méi)有顯式地開(kāi)啟事務(wù),因此這個(gè) update 語(yǔ)句自己就是一個(gè)事務(wù),在執(zhí)行完成后提交事務(wù)時(shí),就會(huì)用到這個(gè)“commit 步驟”。
接下來(lái),我們就一起分析一下在兩階段提交的不同時(shí)刻,MySQL 異常重啟會(huì)出現(xiàn)什么現(xiàn)象。
如果在圖中時(shí)刻 A 的地方,也就是寫入 redo log 處于 prepare 階段之后、寫 binlog 之前,發(fā)生了崩潰(crash),由于此時(shí) binlog 還沒(méi)寫,redo log 也還沒(méi)提交,所以崩潰恢復(fù)的時(shí)候,這個(gè)事務(wù)會(huì)回滾。這時(shí)候,binlog 還沒(méi)寫,所以也不會(huì)傳到備庫(kù)。到這里,我們都可以理解。
而我們理解會(huì)出現(xiàn)問(wèn)題的地方,主要集中在時(shí)刻 B,也就是 binlog 寫完,redo log 還沒(méi) commit 前發(fā)生 crash,那崩潰恢復(fù)的時(shí)候 MySQL 會(huì)怎么處理?
我們先來(lái)看一下崩潰恢復(fù)時(shí)的判斷規(guī)則。
1、如果 redo log 里面的事務(wù)是完整的,也就是已經(jīng)有了 commit 標(biāo)識(shí),則直接提交;
2、如果 redo log 里面的事務(wù)只有完整的 prepare,則判斷對(duì)應(yīng)的事務(wù) binlog 是否存在并完整:
a.如果是,則提交事務(wù);
b.否則,回滾事務(wù)。
這里,時(shí)刻 B 發(fā)生 crash 對(duì)應(yīng)的就是 2(a) 的情況,崩潰恢復(fù)過(guò)程中事務(wù)會(huì)被提交。
現(xiàn)在,我們就針對(duì)兩階段提交再繼續(xù)延展一下。
回答:一個(gè)事務(wù)的 binlog 是有完整格式的:
如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
如果碰到只有 parepare、而沒(méi)有 commit 的 redo log,就拿著 XID 去 binlog 找對(duì)應(yīng)的事務(wù)。
問(wèn)題 3:處于 prepare 階段的 redo log 加上完整 binlog,重啟就能恢復(fù),MySQL 為什么要這么設(shè)計(jì)?
回答:其實(shí),這個(gè)問(wèn)題還是跟我們?cè)诜醋C法中說(shuō)到的數(shù)據(jù)與備份的一致性有關(guān)。在時(shí)刻 B,也就是 binlog 寫完以后 MySQL 發(fā)生崩潰,這時(shí)候 binlog 已經(jīng)寫入了,之后就會(huì)被從庫(kù)(或者用這個(gè) binlog 恢復(fù)出來(lái)的庫(kù))使用。
所以,在主庫(kù)上也要提交這個(gè)事務(wù)。采用這個(gè)策略,主庫(kù)和備庫(kù)的數(shù)據(jù)就保證了一致性。
問(wèn)題 4:如果這樣的話,為什么還要兩階段提交呢?干脆先 redo log 寫完,再寫 binlog。崩潰恢復(fù)的時(shí)候,必須得兩個(gè)日志都完整才可以。是不是一樣的邏輯?
回答:其實(shí),兩階段提交是經(jīng)典的分布式系統(tǒng)問(wèn)題,并不是 MySQL 獨(dú)有的。
如果必須要舉一個(gè)場(chǎng)景,來(lái)說(shuō)明這么做的必要性的話,那就是事務(wù)的持久性問(wèn)題。
對(duì)于 InnoDB 引擎來(lái)說(shuō),如果 redo log 提交完成了,事務(wù)就不能回滾(如果這還允許回滾,就可能覆蓋掉別的事務(wù)的更新)。而如果 redo log 直接提交,然后 binlog 寫入的時(shí)候失敗,InnoDB 又回滾不了,數(shù)據(jù)和 binlog 日志又不一致了。
兩階段提交就是為了給所有人一個(gè)機(jī)會(huì),當(dāng)每個(gè)人都說(shuō)“我 ok”的時(shí)候,再一起提交。
問(wèn)題 5:不引入兩個(gè)日志,也就沒(méi)有兩階段提交的必要了。只用 binlog 來(lái)支持崩潰恢復(fù),又能支持歸檔,不就可以了?
回答:我把這個(gè)問(wèn)題再翻譯一下的話,是說(shuō)只保留 binlog,然后可以把提交流程改成這樣:... -> “數(shù)據(jù)更新到內(nèi)存” -> “寫 binlog” -> “提交事務(wù)”,是不是也可以提供崩潰恢復(fù)的能力?
答案是不可以。
如果說(shuō)歷史原因的話,那就是 InnoDB 并不是 MySQL 的原生存儲(chǔ)引擎。MySQL 的原生引擎是 MyISAM,設(shè)計(jì)之初就有沒(méi)有支持崩潰恢復(fù)。
InnoDB 在作為 MySQL 的插件加入 MySQL 引擎家族之前,就已經(jīng)是一個(gè)提供了崩潰恢復(fù)和事務(wù)支持的引擎了。
InnoDB 接入了 MySQL 后,發(fā)現(xiàn)既然 binlog 沒(méi)有崩潰恢復(fù)的能力,那就用 InnoDB 原有的 redo log 好了。
而如果說(shuō)實(shí)現(xiàn)上的原因的話,就有很多了。就按照問(wèn)題中說(shuō)的,只用 binlog 來(lái)實(shí)現(xiàn)崩潰恢復(fù)的流程,我畫(huà)了一張示意圖,這里就沒(méi)有 redo log 了。
圖 2 只用 binlog 支持崩潰恢復(fù)
這樣的流程下,binlog 還是不能支持崩潰恢復(fù)的。我說(shuō)一個(gè)不支持的點(diǎn)吧:binlog 沒(méi)有能力恢復(fù)“數(shù)據(jù)頁(yè)”。
如果在圖中標(biāo)的位置,也就是 binlog2 寫完了,但是整個(gè)事務(wù)還沒(méi)有 commit 的時(shí)候,MySQL 發(fā)生了 crash。
重啟后,引擎內(nèi)部事務(wù) 2 會(huì)回滾,然后應(yīng)用 binlog2 可以補(bǔ)回來(lái);但是對(duì)于事務(wù) 1 來(lái)說(shuō),系統(tǒng)已經(jīng)認(rèn)為提交完成了,不會(huì)再應(yīng)用一次 binlog1。
但是,InnoDB 引擎使用的是 WAL 技術(shù),執(zhí)行事務(wù)的時(shí)候,寫完內(nèi)存和日志,事務(wù)就算完成了。如果之后崩潰,要依賴于日志來(lái)恢復(fù)數(shù)據(jù)頁(yè)。
也就是說(shuō)在圖中這個(gè)位置發(fā)生崩潰的話,事務(wù) 1 也是可能丟失了的,而且是數(shù)據(jù)頁(yè)級(jí)的丟失。此時(shí),binlog 里面并沒(méi)有記錄數(shù)據(jù)頁(yè)的更新細(xì)節(jié),是補(bǔ)不回來(lái)的。
你如果要說(shuō),那我優(yōu)化一下 binlog 的內(nèi)容,讓它來(lái)記錄數(shù)據(jù)頁(yè)的更改可以嗎?可以,但這其實(shí)就是又做了一個(gè) redo log 出來(lái)。
所以,至少現(xiàn)在的 binlog 能力,還不能支持崩潰恢復(fù)。
問(wèn)題 6:那能不能反過(guò)來(lái),只用 redo log,不要 binlog?
回答:如果只從崩潰恢復(fù)的角度來(lái)講是可以的。你可以把 binlog 關(guān)掉,這樣就沒(méi)有兩階段提交了,但系統(tǒng)依然是 crash-safe 的。
但是,如果你了解一下業(yè)界各個(gè)公司的使用場(chǎng)景的話,就會(huì)發(fā)現(xiàn)在正式的生產(chǎn)庫(kù)上,binlog 都是開(kāi)著的。因?yàn)?binlog 有著 redo log 無(wú)法替代的功能。
一個(gè)是歸檔。redo log 是循環(huán)寫,寫到末尾是要回到開(kāi)頭繼續(xù)寫的。這樣歷史日志沒(méi)法保留,redo log 也就起不到歸檔的作用。
一個(gè)就是 MySQL 系統(tǒng)依賴于 binlog。binlog 作為 MySQL 一開(kāi)始就有的功能,被用在了很多地方。其中,MySQL 系統(tǒng)高可用的基礎(chǔ),就是 binlog 復(fù)制。
還有很多公司有異構(gòu)系統(tǒng)(比如一些數(shù)據(jù)分析系統(tǒng)),這些系統(tǒng)就靠消費(fèi) MySQL 的 binlog 來(lái)更新自己的數(shù)據(jù)。關(guān)掉 binlog 的話,這些下游系統(tǒng)就沒(méi)法輸入了。
總之,由于現(xiàn)在包括 MySQL 高可用在內(nèi)的很多系統(tǒng)機(jī)制都依賴于 binlog,所以“鳩占鵲巢” redo log 還做不到。你看,發(fā)展生態(tài)是多么重要。
最后,推薦你關(guān)注丁奇的《MySQL 實(shí)戰(zhàn) 45 講》專欄。在專欄里,丁奇會(huì)幫你梳理出學(xué)習(xí) MySQL 的主線知識(shí),比如事務(wù)、索引、鎖等,還會(huì)就開(kāi)發(fā)過(guò)程中經(jīng)常遇到的具體問(wèn)題和你分析討論,并且?guī)湍憷斫鈫?wèn)題背后的本質(zhì)。你會(huì)收獲 MySQL 核心技術(shù)詳解與原理說(shuō)明和 36 個(gè) MySQL 常見(jiàn)痛點(diǎn)問(wèn)題解析。
statement 格式的 binlog,最后會(huì)有 COMMIT;
row 格式的 binlog,最后會(huì)有一個(gè) XID event。
另外,在 MySQL 5.6.2 版本以后,還引入了 binlog-checksum 參數(shù),用來(lái)驗(yàn)證 binlog 內(nèi)容的正確性。對(duì)于 binlog 日志由于磁盤原因,可能會(huì)在日志中間出錯(cuò)的情況,MySQL 可以通過(guò)校驗(yàn) checksum 的結(jié)果來(lái)發(fā)現(xiàn)。所以,MySQL 還是有辦法驗(yàn)證事務(wù) binlog 的完整性的。
回答:它們有一個(gè)共同的數(shù)據(jù)字段,叫 XID。崩潰恢復(fù)的時(shí)候,會(huì)按順序掃描 redo log:
看完了這篇文章,相信你對(duì)MySQL中常見(jiàn)的日志問(wèn)題有哪些有了一定的了解,想了解更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。