在DML語句執行的過程當中,主要會涉及到兩個日誌——redo log和bin log,而這兩個日誌是數據庫 WAL (Write Ahead Logging,先寫日誌再寫磁盤提升效率) 技術的兩大主角。下面我來介紹一下這兩個日誌。sql
注意: 這裏爲何說是覆蓋是由於 redo log 寫日誌的特性。redo log 的大小是固定的,因此寫 redo log 是循環的覆蓋寫,你能夠理解爲,一個環形文件以下圖。 數據庫
其中,整個環形是兩個文件構成的(文件個數和文件大小你能夠本身指定),兩個文件像連在一塊兒同樣,其中綠色標識的是 check point ,用來表示當前日誌被清理到的頭(能夠理解爲當前有用的 redo log的頭),而 write position 表明着當前寫入位置(就是當前有用 redo log 的尾)。若是數據庫有更新那麼 write position 就會向前推,若是 write position 要追上 check point 的時候,那麼數據庫就會停下來將 check point 向前推(就是清理,此時就是將內存中的髒頁進行寫入磁盤,對應着上面的釋放時間)。 緩存
bin log 默認是關閉的,須要在配置文件本身設置。併發
若是你對 MySQL 的這兩個日誌沒有了解過的話,上面的特性是很難理解的,若是結合着 DML 語句執行流程就會好理解一點,好比我如今要在數據庫的表中更新 id = 1 這一行中的 value 字段。性能
update table set value = value + 1 where id = 1;
複製代碼
這個時候更新的大體流程就是這樣的優化
注意:這裏的事務提交不只僅是簡單的 commit; ,由於這裏只是簡單的 update 語句,本身自己就是一個事務,因此這裏的 commit; 是隱式的。而這裏所說的 commit; 還包含了 redo log 的狀態轉換——從 prepare 到 commit 狀態,這是一個很重要的點,後面我會詳細解釋,你這裏須要記住有這麼一個東西。 spa
到這裏咱們來簡單總結一下:線程
DML語句的執行和兩個日誌——redo log、bin log有着很大的關係,由於須要提升數據庫的性能,MySQL 採用了一種 WAL(先寫日誌再寫磁盤) 技術,其中就使用到了這兩個日誌。主要的流程以下,MySQL會從內存中獲取相應的數據行(若是沒有先從磁盤 load 到內存中),而後將數據行進行更新並將新行寫入內存後進行redo log的寫入和 bin log 的寫入,在一開始 redo log 是處於 prepare 狀態,只有在 bin log 寫完而後進行事務提交的時候纔會處於 commit 狀態。3d
這個時候你確定有幾個疑問。日誌
redo log是如何保證事務的持久性的?(即當事務執行期間發生 crash ,redo log是如何保證 crash-safe 能力)
bin log是如何完成數據恢復和主從複製的?
上面redo log的 prepare 和 commit 兩個狀態的存在乎義是什麼?
爲何要存在兩個日誌,只要一個不行嗎?
爲何 WAL 技術能提升數據庫性能?
下面我來慢慢回答這些問題。
首先最簡單的是第二個,bin log是如何完成數據恢復和主從複製的?,看了上面的介紹你們應該也知道了,bin log有這麼幾個特性。
因此,當咱們要進行數據恢復的時候能夠 使用 bin log 爲基礎備份出一個和原庫同樣的備庫。當咱們要進行主從複製的時候,可使用 bin log 進行 主從庫的同步。
提醒一下,我這裏使用的是 「雙一配置」(即innodb_flush_log_at_trx_commit = 1 和 sync_binlog = 1 )。sync_binlog = 1的意思是 在事務每次提交的時候都會進行 bin log的持久化。而 innodb_flush_log_at_trx_commit = 1 的意思是事務提交的時候都將 redo log 持久化到磁盤。
因此這裏的雙一就是在每次事務提交的時候都會進行 redo log 和 bin log 的持久化,這兩個參數的其餘配置能夠去參考其餘文章,這裏不作過多涉及。
這就不得不提到兩階段提交了,這時候還會牽扯到上面的另外一個問題redo log的 prepare 和 commit 兩個狀態的存在乎義是什麼?。
兩階段提交是爲了在數據庫發生 crash 以後重啓恢復可以保證事務完整性。好比這個時候咱們正在進行上面的 update 語句,而後此時數據庫宕掉了。爲了你好理解我在將上面的流程圖拿過來。
你會發現,我這裏標註了三個時刻,就是咱們宕機事務可能會執行到的時刻。
首先我先將規則寫在前面,大家能夠對照着去理解。
咱們來看一下上面幾個時刻。
爲何會使用兩階段提交呢?
咱們能夠用反證法。
若是redo log在前 bin log在後,在redo log寫完以後宕機,那麼重啓以後主庫能夠根據 redo log 進行數據恢復,可是這時候由於 bin log 是沒有寫的,因此若是使用 bin log 進行備份,那麼備庫會少了這一個事務。
若是bin log在前 redo log在後,在bin log寫完以後宕機,那麼就會致使後面使用 bin log作備份的時候多出這個事務。
因此若是不使用 兩階段提交 ,那麼就會出現 bin log備份出來的數據庫和原庫的數據不一致。
因此redo log 結合着上面的兩階段提交就解決了,crash-safe 能力和 原庫備庫一致性。
你可能會想,若是不引入兩個日誌就沒有必要進行 兩階段提交 了,這樣豈不是快哉?!
咱們能夠繼續利用反證法去證實。
若是咱們只有 redo log,你知道 redo log 大小是固定且是能夠被覆蓋的,因此若是用來作數據備份是不能夠的,由於它僅僅會記錄當前內存中數據頁的狀況。並且 redo log是 innodb 層面的,它不是 數據庫層面的,若是當你使用的另一個數據庫不是 以 innodb 做爲存儲引擎的話,是根本進行不了同步的。
若是咱們只有 bin log,咱們知道bin log是數據行級別且記錄的是邏輯日誌,因此是沒有「數據頁恢復」的功能的。
因此,這裏咱們仍是須要使用 redo log 和 bin log。
這裏咱們還得引出一個點,咱們上面提到了 redo log 的落盤是在事務執行過程當中。那麼,redo log究竟具體在何時會進行日誌的持久化呢?
具體有三種
這裏咱們提到了兩個很關鍵的詞:redo log buffer 和 page cache。
那麼這個redo log buffer 和 page cache 又是幹什麼的呢?這裏咱們須要將一下 redo log 的三種形態。以下圖
你能夠想一下,一個事務會有多個 DML 語句,而每次 DML 語句都進行寫盤會進行大量的系統調用致使資源浪費和時間浪費,因此每次 DML 語句的時候只是會將 日誌先緩存到內存中的 redo log buffer 中去,而最終調用 commit 的時候會將 redo log buffer 中的內容寫入磁盤。
而 page cache 的存在是爲了加快 fsync 系統調用的速度,咱們知道每次事務 commit 的時候都會進行兩次 fsync 調用(雙一配置),而主要的 redo log 通常會提前進行 write 到文件系統緩衝中,因此這樣會加快寫盤速度。
在這裏,我放了一張加入「緩存」的DML更新流程的圖。
其中 bin log cache你也能夠理解爲緩存,並且由於bin log是邏輯日誌,因此一個事務的bin log是不能被拆開的,因此咱們的 bin log cache 是存放在每一個線程的空間裏的,相互獨立。以下圖
注意:redo log在最後只是 write 進行了寫入文件系統的 page cache 中是由於這個時候已經能夠保證 crash-safe能力了,就不須要再額外進行寫盤操做了,若是不理解能夠結合上面的兩階段提交規則去理解。
因此這裏 redo log 的兩種狀態其實也是兩階段提交的重要組成部分,咱們能夠知道,在bin log未寫盤以前 redo log會先進行寫盤,可是此次寫盤的狀態還只是 prepare 狀態,只有在bin log 寫完以後 纔會最終將狀態變爲 commit,而且這裏再也不進行寫盤操做,而是經過後面進行寫盤的時候順便寫入。
咱們這個時候可能還會有一個疑惑,在「雙一配置」下,每次事務的提交都須要進行兩次 fsync 系統調用,這樣對於數據庫的壓力會是很大的。
咱們知道 WAL 技術能提升數據庫性能的一個緣由是——日誌文件是順序寫的,磁盤的順序寫要比隨機寫快不少。可是對於每次事務進行兩次系統調用這點,WAL 有沒有作什麼優化呢?
答案是有的,試想一下,若是存在多個事務併發的狀況下,此時會出現多個事務的 redo log buffer都已經寫好,這時候 InnoDB 會使用 LSN(log sequence number)日誌邏輯序列號,LSN 是單調遞增的,用來對應 redo log的寫入點,每次寫入 length 長度的 redo log,LSN 就會增長 length。
好比此時有,三個併發事務trx1,trx2,trx3。咱們能夠看到 trx1 是第一個到達的,而 trx1 要進行寫盤的時候已經有三個事務在隊列中了,因此此時 trx1 去寫盤的時候帶的 LSN 就會變成 200,那麼此時進行寫盤,就會將trx1,trx2,trx3都寫入磁盤中了,這裏僅進行了一次系統調用。
因此這裏 WAL 技術會對一些須要系統調用寫盤的地方進行一些優化,儘可能減小IO。對於這個問題就能夠總結爲兩點:
這裏咱們主要介紹了在 MySQL 中 一條 DML 語句是如何執行的,redo log 、bin log又是如何和 DML 語句、事務聯繫在一塊兒的。其中還介紹了 redo log的三種形態和兩種提交狀態,bin log的線程cache,LSN組提交實現等。
總的來講就是 MySQL 在進行 DML 語句的時候會先寫日誌緩存(爲了事務多個 DML 語句而很少次進行寫盤操做),等到事務提交的時候會進行日誌的真正落盤(「雙一配置」),其中還使用了兩階段提交加上redo log的兩種提交狀態來實現 crash-safe能力 和 redo log,bin log 的同步。