你是不是 Redo log 和 Binlog 傻傻分不清楚?
本文轉(zhuǎn)載自微信公眾號(hào)「Java極客技術(shù)」,作者鴨血粉絲 。轉(zhuǎn)載本文請(qǐng)聯(lián)系 Java極客技術(shù)公眾號(hào)。
阿粉這么貼心,肯定給你講清楚了~
在 MySQL 中,特別是存儲(chǔ)引擎使用的是 InnoDB 時(shí),那肯定繞不過去兩個(gè)概念:redo log (重做日志) 和 binlog (二進(jìn)制日志)
簡(jiǎn)單點(diǎn)兒說:redo log 是 InnoDB 存儲(chǔ)引擎層方面的日志,所以如果你使用的存儲(chǔ)引擎不是 InnoDB 的話,那就根本談不上 redo log
binlog 是 MySQL Server 層記錄的日志,所以不管是用的什么存儲(chǔ)引擎,只要是 MySQL 都是會(huì)有 binlog 的存在,在做 MySQL 主從復(fù)制的時(shí)候,利用的就是 binlog
那么,你有疑問嘛?為什么要有 redo log 和 binlog ,只用一個(gè) log 不可以嘛?咱們?cè)敿?xì)來看看它們都分別做了啥
redo log
為什么要有 redo log ?
我們可以這樣想,如果沒有 redo log 的話, MySQL 是如何進(jìn)行工作的 查詢還好說,畢竟只是查詢一下記錄而已,并沒有對(duì)數(shù)據(jù)進(jìn)行更改
那如果是增加和更新操作呢?現(xiàn)在一條 update 語句過來,后面是不是一定會(huì)有限定條件,就比如現(xiàn)在要更新一條記錄,把 A 的銀行卡余額更新到 1k ,那這條語句是不是應(yīng)該來個(gè)限定條件,類似 where userName = 'A',也就是說,一般 update 操作都伴隨著查詢的操作,得先找到這個(gè)人,然后再進(jìn)行更新操作對(duì)吧
如果數(shù)據(jù)量比較小還好,很快就能找到并且更新完畢
但是如果數(shù)據(jù)量比較大,里面有一億條數(shù)據(jù),怎么辦?而且更新操作肯定是要寫到磁盤上去的,那這中間的 IO 成本呢?如果我有好幾十條 update 語句先后更新呢?這樣想的話,你就能想到,就這些操作,成本就高的不行,那能不能降低一下這些成本呢?
這就是 redo log 的作用
就是當(dāng)有一條記錄更新的時(shí)候, InnoDB 引擎就會(huì)先把記錄寫到 redo log 里面去,同時(shí)更新內(nèi)存,這樣就算是更新這條數(shù)據(jù)成功了
但是此時(shí),它并沒有更新到磁盤上去對(duì)吧?別擔(dān)心, InnoDB 會(huì)在恰當(dāng)?shù)臅r(shí)候,把這條及記錄更新到磁盤上去
這樣的思想或者技術(shù),有個(gè)專有名詞: WAL 技術(shù),也就是 WriteAheadLogging ,核心就是先寫日志,再寫磁盤
同樣,這里面有個(gè)問題
redo log 不能一直寫吧?如果更新操作一直寫入到 redo log 中的話,不限制大小的話,可能服務(wù)器上的存儲(chǔ)空間都被 redo log 給占滿了
所以 InnoDB 的 redo log 是固定大小的,比如我們配置了一組 4 個(gè)文件,每個(gè)文件大小是 1GB ,那么它的操作可能就會(huì)這樣:
能夠看到,主要就是 write pos 和 checkpoint , write pos 比較好理解,它就是當(dāng)前記錄的位置,有需要記錄的操作就從當(dāng)前位置向后移,等把 ib_logfile_3 寫完之后,就回到 ib_logfile_0 文件開頭繼續(xù)寫
checkpoint 是當(dāng)前要擦除的位置,就是 InnoDB 引擎不是會(huì)在恰當(dāng)?shù)臅r(shí)候,將這些操作進(jìn)行持久化,更新到磁盤上去,那持久化之后的數(shù)據(jù)是不是就可以擦除了
write pos 和 checkpoint 之間的部分就是可以用來記錄操作的部分,那么如果 write pos 和 checkpoint 相遇了怎么辦?相遇了是不是說明這個(gè)時(shí)候分配的 redo log 大小用完了,那這時(shí)候就不能再進(jìn)行更新操作了,必須停下來處理一下,將 checkpoint 往前推推才行
就是因?yàn)橛辛?redo log ,所以 InnoDB 才可以保證即使數(shù)據(jù)庫發(fā)生了異常重啟,也沒關(guān)系,之前提交的記錄都還在,只需要根據(jù) redo log 里面的記錄進(jìn)行相應(yīng)恢復(fù)就可以了
所以如果你和 DBA 比較熟的話,可以問問,咱們的 MySQL 是不是可以恢復(fù)到半個(gè)月內(nèi)任意一秒的狀態(tài),如果對(duì)方回答是,別懷疑,他真的不是在吹牛逼
binlog
binlog 是 MySQL Server 層的記錄日志,這塊舉個(gè)例子來說吧
在說之前,我們要明白 redo log 和 binlog 的區(qū)別:
- redo log 是 InnoDB 引擎特有的, binlog 是 MySQL 的 Server 層實(shí)現(xiàn)的,所有的引擎都是可以的
- redo log 是物理日志,記錄的是"在 XXX 頁上做了 XXX 修改"; binlog 是邏輯日志,比如" 給 id = 2 這一行的 c 字段加 1"
- redo log 是有固定大小的,所以它的空間會(huì)用完,如果用完的話,一定要進(jìn)行一些寫入磁盤的操作才可以繼續(xù); binlog 是可以追加寫入的,也就是 binlog 沒有空間的概念,一直寫就行了
理解了它們之間區(qū)別之后,我們拿一個(gè)更新操作來舉例
我現(xiàn)在要給 id = 2 這一行的 c 字段加 1,到 MySQL 層面,它是如何去做的呢?
首先,會(huì)先找到這條 id = 2 的數(shù)據(jù),然后找到 c 字段進(jìn)行加 1 操作,這個(gè)時(shí)候,引擎會(huì)將這行數(shù)據(jù)更新到內(nèi)存中,同時(shí)把這個(gè)更新操作記錄到 redo log 里面,這個(gè)時(shí)候 redo log 處于 prepare 狀態(tài),隨后執(zhí)行器生成這個(gè)操作的 binlog ,并且把 binlog 寫入到磁盤完成之后,執(zhí)行器調(diào)用引擎的提交事務(wù)接口,引擎把剛剛寫入的 redo log 從 prepare 狀態(tài)改成 commit 狀態(tài),這樣更新操作才算完成
兩階段提交
在上面的描述中,你能發(fā)現(xiàn) redo log 竟然是先 prepare 狀態(tài),等 binlog 寫完之后,才是 commit 狀態(tài),這種方式就叫"兩階段提交"
為什么會(huì)有這種方式呢?
redo log 和 binlog 都可以用于表示事務(wù)的提交狀態(tài),而兩階段提交就是讓這兩個(gè)狀態(tài)保持邏輯上的一致
可以假設(shè)一下,如果不采用這種方式,而是就先寫 redo log ,再寫 binlog ,會(huì)怎樣?如果在寫 binlog 時(shí),發(fā)生了異常,更新操作已經(jīng)到 redo log 中了,但是此時(shí) binlog 并沒有進(jìn)行更新,是不是出現(xiàn)了數(shù)據(jù)不一致?
先寫 binlog 再寫 redo log 也是一樣的道理
所以,在寫時(shí),先讓 redo log 處于 prepare 狀態(tài),等 binlog 寫完之后,再讓 redo log 處于 commit 狀態(tài),這樣就保持了邏輯上的一致
以上,非常感謝您的閱讀哇~