重新整理 mysql 基礎篇————— 介紹mysql日志[二]


前言

對於后端開發來說,打交道最多的應該是數據庫了,因為你總得把東西存起來。

或是mongodb或者redis又或是mysql。然后你發現一個問題,就是他們都有日志系統,那么這些日志用來干什么的呢?

舉兩個例子,回滾和同步。

回滾,這里的回滾是比如說一條語句增加了1,然后再減一嗎?這里的回滾操作並不是這樣的。

比如說我要更新一條語句,update test set a=1 where b=2,這樣的語句,如果這條語句需要回滾,那么操作就應該是在執行前,先查詢這條數據進行保存,如果執行完畢需要回滾,那么就直接把原來那條語句寫回去。

又比如說,你的數據庫要還原到一個小時前,那么你可以把2個小時前的備份拿出來,然后運行前兩個小時到前一個小時的日志文件,那么這個時候就相當於回到了一個小時前了。

同步,比如說主從同步了,這樣老生常談的了,一般通過事務日志來同步。

總之,有了日志,那么可以幫我們實現很多功能的了。

那么mysql 在innodb 引擎下,有兩個日志非常重要,那就是redo log(重做日志)和 binlog(歸檔日志)日志。

如果沒有這兩個日志,應該沒啥人敢用mysql的了,因為這兩個日志使用了保證mysql的數據完整性的,如果一個數據庫連完整性都不能保證,那么是非常危險的。

正文

redo log

首先看下redo log,這個是什么呢? 這個是innodb存儲引擎的日志。

說一個它的功能哈,前文提及到存儲引擎就相當於我們操作系統的文件系統。

那么問題來了,我們的文件系統是有緩存的,比方說我們寫入一個文件,當我們調用函數的時候,不會直接寫入,而是寫入緩存去,而后又文件系統自己判斷啥時候應該寫入進去。

判斷啥時候應該寫入進去,其中有一個標准就是這次要寫入緩存的時候,判斷緩存是否能夠裝的下,如果裝不下,那么先寫入文件,清除緩存,然后再寫入緩存。

第二個判斷標准就是根據時間某一段時間后進行寫入。

同樣存儲引擎也要為這一段事情操心啊。如果我們更新一條語句,存儲引擎就直接給我們操作正在存儲數據的地方,那效率可想而知。

所以說,存儲引擎就想到一個方法,把更新記錄記錄到redo log 中,等redo log 快寫滿,然后就操作到磁盤,或等空閑時間更新進去。

寫完redo log 之后,就會告訴執行器,執行完畢了。這個時候我們的應用程序得到更新成功的回調。

如果單純只寫入redo log是不行的,因為存儲過程不僅要寫,還要讀啊,如果寫完redo log 通知我們的應用程序更新成功,這個時候還沒寫入到數據文件,那么我們的應用程序去讀的時候就讀到了舊的數據。

那么這個時候,存儲引擎是這么干的。 反正你給我查詢的時候要先查詢內存,內存中沒有才去查詢數據文件。那么存儲引擎,先更新到內存中,然后更新到redo log。這樣對於存儲引擎外部來說,是更新了的,畢竟對於外部來說存儲引擎是一個整體。

這就是MySQL里經常說到的WAL技術,WAL的全稱是Write-Ahead Logging。

那么這個redo log 是一個什么樣的機制呢?難道就一直記錄到一個文件中,然后當要寫入數據文件的時候,全部寫入,然后刪除?

如果這么干效率自然是低了。redo log的機制是這樣的。

redo log 是由四個文件組成,每個文件大小為1G左右,這個都是可以設置的。

有兩個參數,分別為checkpoint 和 write pos。

write pos 是當前記錄的位置。checkpoint 是當前寫入到數據文件的位置。

比如說:

一開始的時候write pos 寫的了第二個文件的問題,如果為位置1000。

這個時候還沒有去正在寫入數據文件,那么這個時候checkpoint 位置就是0。要往數據文件中寫入數據的部分就是checkpoint 到 write pos這一段區間,也就是0-1000位置。

那么這個時候存儲引擎感覺可以更新了,然后開始寫入到正在的數據文件中,那么這個時候開始checkpoints 開始往右移動,假設更新800條。

那么就到了下面這個位置:

這個時候存儲引擎感覺比較忙了,那么就更新800條后,繼續接執行器的任務,那么write pos 往右繼續移動。

那么這個時候就有一個問題出現,比如說文件大小不變,write pos 一直往右移動,這樣會超出啊。

那么這個時候write pos 發現自己到了末尾,人家又從第一個開始寫,覆蓋寫入。

如下:

綠色橫線部分是要更新到數據文件部分。

redo log 還有一個重要的作用,保證數據正在的寫入到數據文件中。

比如說這個時候正在寫入數據文件,然后數據庫異常重啟了,這里理解異常重啟,簡單點理解就是內存都沒了。

那么我們知道寫入文件是有緩存的,如果寫入到一半異常了,那么數據其實是丟了的。

有了redo log之后,只有數據文件flush了,那么這個時候checkpoint才開始偏移,否則就如果異常了內存沒了,那么繼續覆蓋更新,因為checkpoint 沒有變化,那么還是從原來那個異常前的位置開始同步。

那么問題來了,這時候就會想,數據文件是文件,redo log也是文件啊。如果寫入的時候在緩存區,然后宕機這個時候也沒了啊。

沒錯,的確有這個問題,這個時候為了數據安全,redo log直接不使用緩存區。

redo log用於保證crash-safe能力。innodb_flush_log_at_trx_commit這個參數設置成1的時候,表示每次事務的redo log都直接持久化到磁盤。

binlog

binlog 是每個mysql都有的,而不是存儲引擎的東西,屬於mysql 的server 層的東西。

對比一下binlog 和 redo log。

這兩種日志有以下三點不同。

redo log是InnoDB引擎特有的;binlog是MySQL的Server層實現的,所有引擎都可以使用。

redo log是物理日志,記錄的是“在某個數據頁上做了什么修改”;binlog是邏輯日志,記錄的是這個語句的原始邏輯,比如“給ID=2這一行的c字段加1 ”。

redo log是循環寫的,空間固定會用完;binlog是可以追加寫入的。“追加寫”是指binlog文件寫到一定大小后會切換到下一個,並不會覆蓋以前的日志。

一條更新語句在innodb引擎下的更新過程:

1. 執行器先找引擎取ID=2這一行。ID是主鍵,引擎直接用樹搜索找到這一行。如果ID=2這一行所在的數據頁本來就在內存中,就直接返回給執行器;否則,需要先從磁盤讀入內存,然后再返回。

2. 執行器拿到引擎給的行數據,把這個值加上1,比如原來是N,現在就是N+1,得到新的一行數據,再調用引擎接口寫入這行新數據。

3. 引擎將這行新數據更新到內存中,同時將這個更新操作記錄到redo log里面,此時redo log處於prepare狀態。然后告知執行器執行完成了,隨時可以提交事務。

4. 執行器生成這個操作的binlog,並把binlog寫入磁盤。

5. 執行器調用引擎的提交事務接口,引擎把剛剛寫入的redo log改成提交(commit)狀態,更新完成。

這個時候有人可能會問,如果第3步驟先更新到內存,這個時候要是讀取操作而之后redo log沒有寫入就宕機怎么辦?因為是寫入是有鎖的,如果沒有提交事務,這個時候有寫鎖。

這時候就發現一個細節,那就是redo log 一條記錄有兩個狀態一個是prepare,一個是commit狀態。

那么為什么要兩個狀態呢?

我們知道mysql 主從,其實是通過binlog 一條一條發送給從數據庫,讓從數據庫執行binlog里面的操作。

假設沒有這兩個狀態。

假如innodb 寫入redo log之后呢,這個時候數據庫突然宕機了,這個時候redo log 是有的記錄的,這個時候binlog 沒有記錄。

那么innodb 通過redo log 進行寫入到數據文件后,binlog 依然沒有這一條記錄。那么從庫就少了一條操作了。

這個時候主從永遠不可能一致。

如果有了兩個狀態,數據庫重啟后,innodb存儲引擎還是會通知binlog。這時候兩個狀態就保證了binlog里面的數據完整性。

那么這個時候又會問了,假如上面第四步執行了,第五步沒有執行怎么辦?比如宕機了。

是啊,這個時候bin log 中有記錄但是redo log沒有記錄。

那么從庫就少了一條操作記錄了。

這個時候主從永遠不可能一致。同樣,我們如果數據庫退回到某個時間點,如果binlog 和 redolog不一致的話,同樣適用binlog進行回滾一樣的會遇到這個問題。

如果有了redo log 的prepare 狀態,那么如果數據庫重啟的時候檢測到宕機,這個時候redo log里面prepare 狀態的數據就會和binlog里面的數據進行校驗,進而進行恢復。

這種有兩個狀態的提交,叫做兩階段提交。他們起到的作用是如果宕機檢測到異常,就會對比恢復。

同樣binlog也是文件,同樣存在緩存的問題,sync_binlog這個參數設置成1的時候,表示每次事務的binlog都持久化到磁盤。

以上只是個人整理,如有錯誤,望請指點。

下一節事務。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM