binlog
binlog記錄了數據庫表結構和表數據變更,比如update/delete/insert/truncate/create
。它不會記錄select(因爲這沒有對錶沒有進行變更)
binlog我們可以簡單理解爲:存儲着每條變更的SQL語句(當然不止SQL,還有XID「事務Id」等等)
主要有兩個作用:複製和恢復數據
- MySQL在公司使用的時候往往都是一主多從結構的,從服務器需要與主服務器的數據保持一致,這就是通過
binlog
來實現的
主庫把binlog發給從庫,從庫把binlog保存了下來,然後去執行它,這樣就實現了主從同步。 - 數據庫的數據被幹掉了,我們可以通過
binlog
來對數據進行恢復。
假設我們在某一個時間點把一張表中的數據都清空了,可以用binlog來還原。
DBA找到最近一次的全量備份,然後重放從最後一次全量備份到該時間點這段時間的binlog,數據就可以還原回來了。
因爲binlog
記錄了數據庫表的變更,所以我們可以用binlog
進行復制(主從複製)和恢復數據。
redo log
假設我們執行下面一條SQL語句:
update user set name='zhangsan' where id=5;
MySQL執行這條SQL語句,需要先把這條記錄所在的頁找到,然後把頁加載到內存中,最後再對對應的記錄進行修改。(因爲MySQL的基本存儲結構是頁,記錄都存在頁裏)
存在的問題:如果在內存中把數據改了,還沒來得及落磁盤,而此時的數據庫掛了怎麼辦?顯然這次更改就丟了。
但是如果每個請求都需要將數據立馬落磁盤之後,那速度會很慢,MySQL可能也頂不住。所以MySQL是怎麼做的呢?
-
MySQL引入了
redo log
,內存寫完了,然後會寫一份redo log
,這份redo log
記載着這次在某個頁上做了什麼修改。 -
其實寫
redo log
的時候,也會有buffer
,是先寫buffer
,再真正落到磁盤中的。至於從buffer什麼時候落磁盤,會有配置供我們配置。 -
寫
redo log
也是需要寫磁盤的,但它的好處就是順序IO
(我們都知道順序IO比隨機IO快非常多)。 -
所以,
redo log
的存在爲了:當我們修改的時候,寫完內存了,但數據還沒真正寫到磁盤的時候。此時我們的數據庫掛了,我們可以根據redo log
來對數據進行恢復。因爲redo log
是順序IO,所以寫入的速度很快,並且redo log
記載的是物理變化(xxxx頁做了xxx修改),文件的體積很小,恢復速度很快。
redo log寫磁盤比一般的寫磁盤快的原因:
- 一般我們寫磁盤,都是隨機寫,而redo log是順序寫
- MySQL在寫 redo log 上做了優化,比如組提交
binlog和redo log區別
- 存儲的內容
binlog
記載的是update/delete/insert
這樣的SQL語句redo log
記載的是物理修改的內容(xxxx頁修改了xxx)。
我們經常這樣描述:redo log 記錄的是數據的物理變化,binlog 記錄的是數據的邏輯變化。
- 功能
-
redo log
的作用是爲持久化而生的。
寫完內存,如果數據庫掛了,那我們可以通過redo log
來恢復內存還沒來得及刷到磁盤的數據,將redo log
加載到內存裏邊,那內存就能恢復到掛掉之前的數據了。 -
binlog
的作用是複製和恢復而生的。
主從服務器需要保持數據的一致性,通過binlog
來同步數據。
如果整個數據庫的數據都被刪除了,binlog
存儲着所有的數據變更情況,那麼可以通過binlog
來對數據進行恢復。
又看到這裏,你會想:”如果整個數據庫的數據都被刪除了,那我可以用redo log
的記錄來恢復嗎?不能
因爲功能的不同,redo log
存儲的是物理數據的變更,如果我們內存的數據已經刷到了磁盤了,那redo log
的數據就無效了。所以redo log
不會存儲着歷史所有數據的變更,文件的內容會被覆蓋的。
- binlog和redo log 寫入的細節
-
redo log
是MySQL的InnoDB引擎所產生的。 -
binlog
無論MySQL用什麼引擎,都會有的。
InnoDB是有事務的,事務的四大特性之一:持久性就是靠redo log來實現的(如果寫入內存成功,但數據還沒真正刷到磁盤,如果此時的數據庫掛了,我們可以靠redo log來恢復內存的數據,這就實現了持久性)。
上面也提到,在修改的數據的時候,binlog
會記載着變更的類容,redo log
也會記載着變更的內容。(只不過一個存儲的是物理變化,一個存儲的是邏輯變化)。那他們的寫入順序是什麼樣的呢?
redo log
事務開始的時候,就開始記錄每次的變更信息- binlog是在事務提交的時候才記錄。
於是新有的問題又出現了:我寫其中的某一個log,失敗了,那會怎麼辦?現在我們的前提是先寫redo log
,再寫binlog
,我們來看看:
- 如果寫
redo log
失敗了,那我們就認爲這次事務有問題,回滾,不再寫binlog。 - 如果寫
redo log
成功了,寫binlog
,寫binlog寫一半了,但失敗了怎麼辦?我們還是會對這次的事務回滾,將無效的binlog
給刪除(因爲binlog會影響從庫的數據,所以需要做刪除操作) - 如果寫
redo log
和binlog
都成功了,那這次算是事務纔會真正成功。
簡單來說:MySQL需要保證redo log
和binlog
的數據是一致的,如果不一致,那就亂套了。
- 如果
redo log
寫失敗了,而binlog
寫成功了。那假設內存的數據還沒來得及落磁盤,機器就掛掉了。那主從服務器的數據就不一致了。(從服務器通過binlog
得到最新的數據,而主服務器由於redo log
沒有記載,沒法恢復數據) - 如果
redo log
寫成功了,而binlog
寫失敗了。那從服務器就拿不到最新的數據了。
兩階段提交
最後,那麼當我執行一條 update
語句時,redo log
和 binlog
是在什麼時候被寫入的呢?這就有了我們常說的「兩階段提交」:
- 寫入:
redo log
(prepare) - 寫入:
binlog
- 寫入:
redo log
(commit)
爲什麼 redo log 要分兩個階段: prepare 和 commit ?redo log 就不能一次寫入嗎?
我們分兩種情況討論:
- 先寫 redo log,再寫 binlog
- 先寫 binlog,再寫 redo log
-
先寫 redo log,再寫 binlog
這樣會出現 redo log 寫入到磁盤了,但是 binlog 還沒寫入磁盤,於是當發生 crash recovery 時,恢復後,主庫會應用 redo log,恢復數據,但是由於沒有 binlog,從庫就不會同步這些數據,主庫比從庫“新”,造成主從不一致 -
先寫 binlog,再寫 redo log
這樣造成從庫比主庫“新”,也會造成主從不一致
而兩階段提交,就解決這個問題,crash recovery 時:
- 如果 redo log 已經 commit,那毫不猶豫的,把事務提交
- 如果 redo log 處於 prepare,則去判斷事務對應的 binlog 是不是完整的
– 是,則把事務提交
– 否,則事務回滾
兩階段提交,其實是爲了保證 redo log 和 binlog 的邏輯一致性。
undo log
undo log
主要有兩個作用:回滾和多版本控制(MVCC)
在數據修改的時候,不僅記錄了redo log
,還記錄undo log
,如果因爲某些原因導致事務失敗或回滾了,可以用undo log
進行回滾
undo log主要存儲的也是邏輯日誌,比如我們要insert
一條數據了,那undo log會記錄的一條對應的delete
日誌。我們要update
一條記錄時,它會記錄一條對應相反的update記錄。
這也應該容易理解,畢竟回滾嘛,跟需要修改的操作相反就好,這樣就能達到回滾的目的。因爲支持回滾操作,所以我們就能保證:“一個事務包含多個操作,這些操作要麼全部執行,要麼全都不執行”。【原子性】
因爲undo log存儲着修改之前的數據,相當於一個前版本,MVCC實現的是讀寫不阻塞,讀的時候只要返回前一個版本的數據就行了。