Mysql日誌 寫入機制

2021-10-05 07:27:13 字數 3539 閱讀 6717

事務執行過程中,先把日誌寫到binlog cache,事務提交的時候,再把binlog cache寫到binlog檔案中。

乙個事務的binlog是不能被拆開的,因此不論這個事務多大,也要確保一次性寫入。

系統給binlog cache分配了一片記憶體,每個執行緒乙個,引數 binlog_cache_size用於控制單個執行緒內binlog cache所佔記憶體的大小。如果超過了這個引數規定的大小,就要暫存到磁碟。

事務提交的時候,執行器把binlog cache裡的完整事務寫入到binlog中,並清空binlog cache。

每個執行緒有自己binlog cache,但是共用同乙份binlog檔案。

圖中的write,指的就是指把日誌寫入到檔案系統的page cache,並沒有把資料持久化到磁碟,所以速度比較快。

圖中的fsync,才是將資料持久化到磁碟的操作。一般情況下,我們認為fsync才佔磁碟的iops。

write 和fsync的時機,是由引數sync_binlog控制的:

1.sync_binlog=0的時候,表示每次提交事務都只write,不fsync;

2.sync_binlog=1的時候,表示每次提交事務都會執行fsync;

3sync_binlog=n(n>1)的時候,表示每次提交事務都write,但累積n個事務後才fsync。

因此,在出現io瓶頸的場景裡,將sync_binlog設定成乙個比較大的值,可以提公升效能。在實際的業務場景中,考慮到丟失日誌量的可控性,一般不建議將這個引數設成0,比較常見的是將其設定為100~1000中的某個數值。

但是,將sync_binlog設定為n,對應的風險是:如果主機發生異常重啟,會丟失最近n個事務的binlog日誌。

redo log可能存在的三種狀態說起:

這三種狀態分別是:

1.存在redo log buffer中,物理上是在mysql程序記憶體中,就是圖中的紅色部分;

2.寫到磁碟(write),但是沒有持久化(fsync),物理上是在檔案系統的page cache裡面,也就是圖中的黃色部分;

3.持久化到磁碟,對應的是hard disk,也就是圖中的綠色部分。

為了控制redo log的寫入策略,innodb提供了innodb_flush_log_at_trx_commit引數,它有三種可能取值:

1.設定為0的時候,表示每次事務提交時都只是把redo log留在redo log buffer中;

2.設定為1的時候,表示每次事務提交時都將redo log直接持久化到磁碟;

3.設定為2的時候,表示每次事務提交時都只是把redo log寫到page cache。

乙個沒有提交的事務的redo log寫入到磁碟中,三種場景:

1.innodb有乙個後台執行緒,每隔1秒,就會把redo log buffer中的日誌,呼叫write寫到檔案系統的page cache,然後呼叫fsync持久化到磁碟。

2.redo log buffer占用的空間即將達到 innodb_log_buffer_size一半的時候,後台執行緒會主動寫盤。注意,由於這個事務並沒有提交,所以這個寫盤動作只是write,而沒有呼叫fsync,也就是只留在了檔案系統的page cache。

3.並行的事務提交的時候,順帶將這個事務的redo log buffer持久化到磁碟。假設乙個事務a執行到一半,已經寫了一些redo log到buffer中,這時候有另外乙個執行緒的事務b提交,如果innodb_flush_log_at_trx_commit設定的是1,那麼按照這個引數的邏輯,事務b要把redo log buffer裡的日誌全部持久化到磁碟。這時候,就會帶上事務a在redo log buffer裡的日誌一起持久化到磁碟。

如果把innodb_flush_log_at_trx_commit設定成1,那麼redo log在prepare階段就要持久化一次,因為有乙個崩潰恢復邏輯是要依賴於prepare 的redo log,再加上binlog來恢復的。

每秒一次後台輪詢刷盤,再加上崩潰恢復這個邏輯,innodb就認為redo log在commit的時候就不需要fsync了,只會write到檔案系統的page cache中就夠了。

日誌邏輯序列號lsn是單調遞增的,用來對應redo log的乙個個寫入點。每次寫入長度為length的redo log, lsn的值就會加上length。

lsn也會寫到innodb的資料頁中,來確保資料頁不會被多次執行重複的redo log。

三個併發事務(trx1, trx2, trx3)在prepare 階段,都寫完redo log buffer,持久化到磁碟的過程,對應的lsn分別是50、120 和160。

從圖中可以看到,

1.trx1是第乙個到達的,會被選為這組的 leader;

2.等trx1要開始寫盤的時候,這個組裡面已經有了三個事務,這時候lsn也變成了160;

3.trx1去寫盤的時候,帶的就是lsn=160,因此等trx1返回時,所有lsn小於等於160的redo log,都已經被持久化到磁碟;

4.這時候trx2和trx3就可以直接返回了。

所以,一次組提交裡面,組員越多,節約磁碟iops的效果越好。但如果只有單執行緒壓測,那就只能老老實實地乙個事務對應一次持久化操作了。

在併發更新場景下,第乙個事務寫完redo log buffer以後,接下來這個fsync越晚呼叫,組員可能越多,節約iops的效果就越好。

實際上,寫binlog是分成兩步的:

1.先把binlog從binlog cache中寫到磁碟上的binlog檔案;

2.呼叫fsync持久化。

mysql為了讓組提交的效果更好,把redo log做fsync的時間拖到了步驟1之後。這麼一來,binlog也可以組提交了。在把binlog fsync到磁碟時,如果有多個事務的binlog已經寫完了,也是一起持久化的,這樣也可以減少iops的消耗。

提公升binlog組提交的效果:

1.binlog_group_commit_sync_delay引數,表示延遲多少微秒後才呼叫fsync;

2.binlog_group_commit_sync_no_delay_count引數,表示累積多少次以後才呼叫fsync。

針對這個問題,可以考慮以下三種方法:

1.設定 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count引數,減少binlog的寫盤次數。這個方法是基於「額外的故意等待」來實現的,因此可能會增加語句的響應時間,但沒有丟失資料的風險。

2.將sync_binlog 設定為大於1的值(比較常見是100~1000)。這樣做的風險是,主機掉電時會丟binlog日誌。

3.將innodb_flush_log_at_trx_commit設定為2。這樣做的風險是,主機掉電的時候會丟資料。

[資料**]

1.mysql實戰45講-丁奇

flask將日誌寫入日誌檔案

import logging logging.basicconfig level logging.debug,控制台列印的日誌級別 filename log new.log 將日誌寫入log new.log檔案中 filemode a 模式,有w和a,w就是寫模式,每次都會重新寫日誌,覆蓋之前的日誌...

日誌寫入到檔案 多檔案寫入

在上篇文章的基礎上,修改配置 log4j.rootlogger info,fout 說明 rootlogger是可以多樣式定義的,如log4j.rootlogger info,myout,fout。myout,fout是自定義樣式。這樣就會在c盤下建立a.log檔案。有這樣一種需求,不同模組需要各自...

java寫入日誌檔案

首先要定義日誌的四個等級 info,debug,error,fatal,還要建日誌輸出方式,如console,file,html,database 以上資訊可以放到.properties檔案中。建類,來實現各種方式的輸出。如 private static log log new log privat...