單點突破 MySQL之日誌

2022-06-23 13:57:12 字數 4720 閱讀 7789

開發環境:mysql5.7.31

日誌是 mysql 資料庫的重要組成部分,記錄著資料庫執行期間各種狀態資訊。若資料庫發生故障,可通過不同日誌記錄恢復資料庫的原來資料。因此實際上日誌系統直接決定著mysql執行的魯棒性和穩健性。mysql中有六種日誌檔案,分別是:重做日誌(redo log)、回滾日誌(undo log)、二進位制日誌(binlog)、錯誤日誌(errorlog)、慢查詢日誌(slow query log)、一般查詢日誌(general log),中繼日誌(relay log)。

其中開發中比較關注的是重做日誌(redo log)、回滾日誌(undo log)、二進位制日誌(binlog),它們也是面試中關於mysql日誌的常客。

重做日誌(redo log)是innodb引擎層的日誌,用來記錄事務操作引起資料的變化,記錄的是資料頁的物理修改。作用是:確保事務的永續性。防止在發生故障的時間點,尚有髒頁未寫入磁碟,在重啟mysql服務的時候,根據redo log進行重做,從而達到事務的永續性這一特性。

在 mysql 中,如果每一次的更新操作都需要寫進磁碟,然後磁碟也要找到對應的那條記錄,然後再更新,整個過程 io 成本、查詢成本都很高。為了解決這個問題,mysql 的設計者就採用了日誌(redo log)來提升更新效率。

而日誌和磁碟配合的整個過程,其實就是 mysql 裡的 wal 技術,wal 的全稱是 write-ahead logging,這就是所謂的預寫式技術,它的關鍵點就是先寫日誌,再寫磁碟這種技術可以大大減少io操作的頻率,提升資料重新整理的效率。

具體來說,當有一條記錄需要更新的時候,innodb 引擎就會先把記錄寫到 redo log(redolog buffer)裡面,並更新記憶體(buffer pool),這個時候更新就算完成了。同時,innodb 引擎會在適當的時候(如系統空閒時),將這個操作記錄更新到磁碟裡面(刷髒頁)。

我們都知道,事務的四大特性裡面有一個是永續性,具體來說就是隻要事務提交成功,那麼對資料庫做的修改就被永久儲存下來了,不可能因為任何原因再回到原來的狀態。

那麼mysql是如何保證一致性的呢?

最簡單的做法是在每次事務提交的時候,將該事務涉及修改的資料頁全部重新整理到磁碟中。但是這麼做會有嚴重的效能問題,主要體現在兩個方面:

因此 mysql 設計了 redo log ,具體來說就是隻記錄事務對資料頁做了哪些修改,這樣就能完美地解決效能問題了(相對而言檔案更小並且是順序io)。

redo log 包括兩部分:一個是記憶體中的日誌緩衝( redo log buffer),另一個是磁碟上的日誌檔案(redo logfile)。

mysql 每執行一條 dml 語句,先將記錄寫入 redo log buffer,後續某個時間點再一次性將多個操作記錄寫到 redo log file。這種技術就是上文提到的 wal(write-ahead logging) 技術。

在計算機作業系統中,使用者空間( user space )下的緩衝區資料一般情況下是無法直接寫入磁碟的,中間必須經過作業系統核心空間( kernel space )緩衝區( os buffer )。因此, redo log buffer 寫入 redo logfile 實際上是先寫入 os buffer ,然後再通過系統呼叫 fsync() 將其刷到 redo log file中,具體如下圖:

看起來很複雜,但只要記住redo log是先寫日誌再寫磁碟就行,這些只是redo log的執行過程,而且上述的過程更接近髒日誌刷盤,如果看不明白上面的知識,我們下面從兩個方面展開說明redo log

我們上面說到的過程就是髒日誌刷盤,上文提到了幾個概念,這裡做一些補充:

為了確保每次記錄都能夠寫入到磁碟中的日誌中,每次將redo log buffer中的日誌寫入redo log file的過程中都會呼叫一次作業系統的fsync操作。在寫入的過程中,還需要經過作業系統核心空間的os buffer。也就是我們上圖的操作了。

髒資料:指記憶體中未刷到磁碟的資料。

redo log的記錄形式如下:

redo log 實際上記錄資料頁的變更,而這種變更記錄是沒必要全部儲存,因此 redo log實現上採用了大小固定,迴圈寫入的方式,當寫到結尾時,會回到開頭迴圈寫日誌。

redo log日誌的大小是固定的,為了能夠持續不斷的對更新記錄進行寫入,在redo log日誌中設定了兩個標誌位置,checkpoint和write_pos,分別表示記錄擦除的位置和記錄寫入的位置。

redo log中最重要的概念就是緩衝池buffer pool,這是在記憶體中分配的一個區域,包含了磁碟中部分資料頁的對映,作為訪問資料庫的緩衝。

因此,當資料修改時,除了修改buffer pool中的資料,還會在redo log中記錄這次操作;當事務提交時,會根據redo log的記錄對資料進行刷盤。如果mysql宕機,重啟時可以讀取redo log中的資料,對資料庫進行恢復,從而保證了事務的永續性,使得資料庫獲得crash-safe能力。

二進位制日誌binlog是服務層的日誌,還被稱為歸檔日誌。binlog主要記錄資料庫的變化情況,內容包括資料庫所有的更新操作。所有涉及資料變動的操作,都要記錄進二進位制日誌中,以二進位制的形式儲存在磁碟中。因此有了binlog可以很方便的對資料進行複製和備份,因而也常用作主從庫的同步

binlog 是 mysql的邏輯日誌,並且由 server 層進行記錄,使用任何儲存引擎的 mysql 資料庫都會記錄 binlog 日誌。

binlog 是通過追加的方式進行寫入的,可以通過max_binlog_size 引數設定每個 binlog檔案的大小,當檔案大小達到給定值之後,會生成新的檔案來儲存日誌。

在實際應用中, binlog 的主要使用場景有兩個,分別是主從複製資料恢復

binlog 日誌有三種格式,分別為 statment 、 row 和 mixed。

在 mysql 5.7.7 之前,預設的格式是 statement , mysql 5.7.7 之後,預設值是 row。日誌格式通過 binlog-format 指定。

row:基於行的複製(row-based replication, rbr ),不記錄每條sql語句的上下文資訊,僅需記錄哪條資料被修改了 。

mixed:基於statment 和 row 兩種模式的混合複製(mixed-based replication, mbr ),一般的複製使用statement 模式儲存 binlog ,對於 statement 模式無法複製的操作使用 row 模式儲存 binlog

在mysql執行更新語句時,都會涉及到redo log日誌和binlog日誌的讀寫。一條更新語句的執行過程如下:

從上圖可以看出,mysql在執行更新語句的時候,在服務層進行語句的解析和執行,在引擎層進行資料的提取和儲存;同時在服務層對binlog進行寫入,在innodb內進行redo log的寫入。不僅如此,在對redo log寫入時有兩個階段的提交,一是binlog寫入之前prepare狀態的寫入,二是binlog寫入之後commit狀態的寫入。兩階段提交可以保證binlog和redo log中儲存的資訊是一致的。

這裡binlog所儲存的內容看起來似乎與redo log很相似,但是其實不然。redo log是一種物理日誌,記錄的是實際上對某個資料進行了怎麼樣的修改;而binlog是邏輯日誌,記錄的是sql語句的原始邏輯,比如”給id=2這一行的a欄位加1 "。binlog日誌中的內容是二進位制的,根據日記格式引數的不同,可能基於sql語句、基於資料本身或者二者的混合。一般常用記錄的都是sql語句。

同時,redo log是基於crash recovery,保證mysql宕機後的資料恢復;而binlog是基於point-in-time recovery,保證伺服器可以基於時間點對資料進行恢復,或者對資料進行備份。

總的來說,兩者的區別可以歸納如下:

對引擎的支援

檔案空間

寫入方式

由 binlog 和 redo log 的區別可知:binlog 日誌只用于歸檔,只依靠 binlog 是沒有 crash-safe 能力的。

但只有 redo log 也不行,因為 redo log 是 innodb特有的,且日誌上的記錄落盤後會被覆蓋掉。因此需要 binlog和 redo log二者同時記錄,才能保證當資料庫發生宕機重啟時,資料不會丟失。

回滾日誌同樣也是innodb引擎提供的日誌,顧名思義,回滾日誌的作用就是對資料進行回滾。當事務對資料庫進行修改,innodb引擎不僅會記錄redo log,還會生成對應的undo log日誌;如果事務執行失敗或呼叫了rollback,導致事務需要回滾,就可以利用undo log中的資訊將資料回滾到修改之前的樣子。

但是undo log不redo log不一樣,它屬於邏輯日誌。它對sql語句執行相關的資訊進行記錄。當發生回滾時,innodb引擎會根據undo log日誌中的記錄做與之前相反的工作。比如對於每個資料插入操作(insert),回滾時會執行資料刪除操作(delete);對於每個資料刪除操作(delete),回滾時會執行資料插入操作(insert);對於每個資料更新操作(update),回滾時會執行一個相反的資料更新操作(update),把資料改回去。undo log由兩個作用,一是提供回滾,二是實現mvcc。