MySql日誌系統介紹

1、前言

前一章通過一條簡單的查詢語句總結了mysql整體的框架,這一章我們通過一條更新語句來總結一下mysql日誌系統。

2 、相關知識

上一章我們介紹了,一條查詢語句的執行過程是經過連接器、分析器、優化器、執行器,最後到達存儲引擎。
那一條更新語句的流程是怎樣的呢?下面我們通過一條簡單的更新語句來介紹一下:

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

這是一條非常簡單的更新一句,學過mysql的人都不陌生。下面我們把上一章的圖搬過來,其實查詢語句走的流程,更新語句照樣會走一遍。
在這裏插入圖片描述
步驟如下:

第一步:連接數據庫,這是連接器的工作
第二步:分析器會詞法分析,語句分析,告訴你這條語句是幹什麼的(這是一條更新語句)。
注意:此時不但不走緩存還會把表T中所有的緩存清空,這也是我們不建議使用緩存的原因。
第三步:優化器決定使用ID這個索引
第四步:執行器負責具體執行,找到這一行,然後更新。

與查詢流程不一樣的是:

1.更新不走緩存,反而會把緩存清楚掉
2.更新流程還設計二個重要的日誌模塊:redo log(重做日誌) 和 bin log(歸檔日誌)

2.1 重要的日誌:redo log 介紹

《孔乙己》這篇文章都學過吧,裏面有這麼一個情節:

酒店掌櫃有一個粉板,專門用來記錄客人的賒賬記錄。
如果賒賬的人不多,那麼他可以把顧客名和賬目寫在板上。
但如果賒賬的人多了,粉板總會有記不下的時候,這個時候掌櫃一定還有一個專門記錄賒賬的賬本。

這個場景大家能想象的到吧,假設現在有人要社長,掌櫃一般有二種做法:

	第一種做法:直接把賬本翻出來,把這次賒的賬加上去或者扣除掉;
    第二種做法:是先在粉板上記下這次的賬,等打烊以後再把賬本翻出來,把粉筆板上的賬覈算進賬本中。

大家想一下哪種方法好呢?如果每天僅僅有很少客戶的時候,第一種方法比較合適,但是如果酒店生意紅火的話,明顯第二中比較合適,因爲首先,你得找到這個人的賒賬總額那條記錄。你想想,密密麻麻幾十頁,掌櫃要找到那個名字,可能還得帶上老花鏡慢慢找,找到之後再拿出算盤計算,最後再將結果寫回到賬本上。這整個過程想想都麻煩。相比之下,還是先在粉板上記一下方便。你想想,如果掌櫃沒有粉板的幫助,每次記賬都得翻賬本,效率是不是低得讓人難以忍受?

Mysql中也有這個問題,我們的磁盤就好比掌櫃的賬本,在磁盤中找一條記錄,再更新成本也是非常高的。那怎麼辦,Mysql就使用了和小粉板同樣的思路來解決問題。接下來我們做個對應:

1.小粉板----------內存
2.賬本-------------磁盤數據

做這個對應關係,是希望對大家接下來的理解有幫助。在MySql中對這個技術有一個專業名詞:WAL(Write-Ahead Loggin),它的關鍵點就是先寫日誌,再寫磁盤,也就是先寫粉板,等不忙的時候再整理到賬本中。

具體來說,當一條記錄需要更新時,MySql是這樣做的:

	第一步:InnoDB 引擎就會先把記錄寫到 redo log(粉板) 裏面,並更新內存,這個時候更新就算完成了
	第二步:InnoDB 引擎會在適當的時候,將這個操 作記錄更新到磁盤裏面,而這個更新往往是在系統比較空閒的時候做,這就像打烊以後掌櫃做的事。

但是聰明的你會有問題了:

如果今天的客戶特別多,導致小粉板寫滿了,但是還來不及忘賬本上記怎麼辦?

解決辦法有二種:

第一種:暫停營業,先把粉板中的字記錄到賬本上,把粉板中的字擦掉,再開始營業
第二種:找2個粉板,第一粉板滿了就要記錄到賬本中,如果這個時候有客戶來了,暫時記錄到第二個粉板上,等第一個粉板整理到賬本中後把第一個份板擦掉。這樣二塊粉板輪換着用。

想一下是不是第2種方法好,MySql同樣是用了第二種方法。InnoDB 的 redo log 是固定大小的,比如可以配置爲一組 4 個文件,每個文件的大 小是 1GB,那麼這塊“粉板”總共就可以記錄 4GB 的操作。從頭開始寫,寫到末尾就又回到開 頭循環寫,如下面這個圖所示:

在這裏插入圖片描述

 1.wirte pos 是當前記錄的位置,一邊寫一邊後移,寫到第 3 號文件末尾後就回到 0 號文件開頭。
 2.checkpoint 是當前要擦除的位置,也是往後推移並且循環的,便是擦除記錄前要把記錄更新到數據文件。你可以把checkpoint想像成破壞着,他所指的地方寸草不生。

注意的地方:

1.write pos 和 checkpoint 之間的是“粉板”上還空着的部分,可以用來記錄新的操作。
2.如果 write pos 追上 checkpoint,表示“粉板”滿了,這時候不能再執行新的更新,得停下來先擦 掉一些記錄,把 checkpoint 推進一下。

名詞解釋

	crash-safe :有了 redo log,InnoDB 就可以保證即使數據庫發生異常重啓,之前提交的記錄都不會丟失, 這個能力稱爲crash-safe

有了crash-safe之後,我們能保證數據的安全,舉個例子:只要賒賬記錄記在了粉板上 或寫在了賬本上,之後即使掌櫃忘記了,比如突然停業幾天,恢復生意後依然可以通過賬本和粉 板上的數據明確賒賬賬目。

2.2 重要的日誌:binlog 介紹

前面我們說過Mysql其實有二個日誌的。

	一塊是server層的,主要做的是Mysql功能層面的事情,bin log 就是Server 層日誌,所有引擎都可以使用的。
	二塊是引擎層的,主要負責存儲相關的事宜。redo log 就是 InnoDB 引擎特有的日誌,記住只有InnoDB有

調皮如你肯定會問redo log 就很強大了,爲啥還要二個日誌系統呢?!這是歷史問題,要解釋這個問題就要往前追溯Mysql的歷史了。

最開始的Mysql用的不是InnoDB引擎,而是MyISAM引擎,那時的MyISAM是沒有crash-safe能力的,binlog只能用於歸檔。而InnoDB引入Mysql後,要克服Mysql沒有crash-safe的能力,就引入了現在的redo log日誌。

比較二個日誌的三點不同

1.redo log 是InnoDB引擎所特有的;binlog是MySql的server層實現的,所有引擎都可使用
2.redo log是物理日誌,記錄的是“在某個數據頁上做了什麼修改”;binlog是邏輯日誌,記錄的是這個語句的原始邏輯,比如“給ID=2這一行的c字段加1”。
3.redo是循環寫,因爲空間是固定的早晚會用完;binlog是追加寫,即一個文件寫滿後切換到下一個,並不會覆蓋以前的日誌。

有了對這兩個日誌的概念性理解,我們再來看執行器和 InnoDB 引擎在執行這個簡單的 update 語句時的內部流程。

1. 執行器先找引擎取 ID=2 這一行。ID 是主鍵,引擎直接用樹搜索找到這一行。如果 ID=2 這一行所在的數據頁本來就在內存中,就直接返回給執行器;否則,需要先從磁盤讀入內存,然後再返回。
2. 執行器拿到引擎給的行數據,把這個值加上 1,比如原來是 N,現在就是 N+1,得到新的 一行數據,再調用引擎接口寫入這行新數據。
3. 請注意此時引擎並沒有寫入磁盤中,而是引擎將這行新數據更新到內存中,同時將這個更新操作記錄到 redo log 裏面,此時 redo log 處於 prepare 狀態。然後告知執行器執行完成了,隨時可以提交事務。
4. 執行器生成這個操作的 binlog,並把 binlog 寫入磁盤。
5. 執行器調用引擎的提交事務接口,引擎把剛剛寫入的 redo log 改成提交(commit)狀 態,更新完成。

這裏我給出這個 update 語句的執行流程圖,圖中淺色框表示是在 InnoDB 內部執行的,深色框表示是在執行器中執行的。

在這裏插入圖片描述
你可能注意到了,最後三步看上去有點“繞”,將 redo log 的寫入拆成了兩個步驟:prepare 和 commit,這就是"兩階段提交"。

重點理解二個提交階段prepare 和 commit

不要被二個日誌給帶歪了,從二個提交狀態來理解。
我們來說一下,如果重啓會不會帶來問題:

	1.在寫入redolog處於prepare階段後,還沒來的及寫binlog時重啓
		只要處於prepare狀態,mysql重啓後發現是prepare狀態的數據,就會認爲是無效數據,所以不會產生影響
	2.redolog處於prepare階段,binlog也寫完並寫入磁盤,此時還沒commit就重啓。
		這個時候redolog是prepare, binlog也已經完整了,Mysql重啓後,認爲認可這一個事務,會提交掉。

二個階段提交有大作用

不只是誤操作後需要用這個過程來恢復數據。當你需要擴容的時候,也就是需要再 多搭建一些備庫來增加系統的讀能力的時候,現在常見的做法也是用全量備份加上應用 binlog 來實現的,這個“不一致”就會導致你的線上出現主從數據庫不一致的情況。
簡單說,redo log 和 binlog 都可以用於表示事務的提交狀態,而兩階段提交就是讓這兩個狀態 保持邏輯上的一致。

小結

今天,我介紹了 MySQL 裏面最重要的兩個日誌,即物理日誌 redo log 和邏輯日誌 binlog。
redo log 用於保證 crash-safe 能力。innodb_flush_log_at_trx_commit 這個參數設置成 1 的 時候,表示每次事務的 redo log 都直接持久化到磁盤。這個參數我建議你設置成 1,這樣可以 保證 MySQL 異常重啓之後數據不丟失。
sync_binlog 這個參數設置成 1 的時候,表示每次事務的 binlog 都持久化到磁盤。這個參數我 也建議你設置成 1,這樣可以保證 MySQL 異常重啓之後 binlog 不丟失。
我還跟你介紹了與 MySQL 日誌系統密切相關的“兩階段提交”。兩階段提交是跨系統維持數據 邏輯一致性時常用的一個方案,即使你不做數據庫內核開發,日常開發中也有可能會用到。

結尾

好了就講到這裏吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章