Mysql保護資料不丟失機制介紹

2021-10-14 03:51:35 字數 4394 閱讀 6322

目錄

1 binlog的寫入機制

2 redo log的寫入機制

3 組提交

3.1 日誌邏輯序列號(log sequence number,lsn)

3.2 組提交

binlog的寫入邏輯比較簡單:事務執行過程中,先把日誌寫到binlog cache,事務提交的時候,再把binlog cache寫到binlog檔案中。

乙個事務的binlog是不能被拆開的,不論事務有多大,也要確保一次性寫入。這就涉及到了binlog cache的儲存問題。

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

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

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

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

sync_binlog=0,表示每次提交事務都只write,不fsync;

sync_binlog=1,表示每次提交事務都會執行fsync;

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

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

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

事務在執行過程中,生成的redo log是要先寫到redo log buffer。

redo log buffer裡面的內容,是不是每次生成後都要直接持久化到磁碟呢?

如果事務執行期間mysql發生異常重啟,那這部分日誌就丟了。由於事務並沒有提交,所以這時日誌丟了也不會有損失。

事務還沒提交的時候,redo log buffer中的部分日誌有沒有可能被持久化到磁碟呢?

這個問題,要從redo log可能存在的三種狀態說起,對應圖2中的三個顏色塊。

這三種狀態分別是:

存在redo log buffer中,物理上是在mysql程序記憶體中;

寫到磁碟(write),但是沒有持久化(fsync),物理上是在檔案系統的page cache裡面;

持久化到磁碟,對應的是hard disk。

日誌寫到redo log buffer是很快的,wirte到page cache也比較快,但是持久化到磁碟的速度就很慢。

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

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

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

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

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

注意:事務執行中間過程的redo log也是直接寫在redo log buffer中的,這些redo log也會被後台執行緒一起持久化到磁碟。也就是說,乙個沒有提交的事務的redo log,也是可能已經持久化到磁碟的。

除了後台執行緒每秒一次的輪詢操作外,還有兩種場景會讓乙個沒有提交的事務的redo log寫入到磁碟中:

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

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

兩階段提交的時候說過,時序上redo log先prepare, 再寫binlog,最後再把redo log commit。如果把innodb_flush_log_at_trx_commit設定成1,那麼redo log在prepare階段就要持久化一次,因為有乙個崩潰恢復邏輯是要依賴於prepare 的redo log,再加上binlog來恢復的。

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

通常我們說mysql的「雙1」配置,指的就是sync_binlog和innodb_flush_log_at_trx_commit都設定成 1。也就是說,乙個事務完整提交前,需要等待兩次刷盤,一次是redo log(prepare 階段),一次是binlog。

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

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

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

從圖中可以看到,

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

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

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

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

一次組提交裡面,組員越多,節約磁碟iops的效果越好。

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

為了讓一次fsync帶的組員更多,mysql有乙個很有趣的優化:拖時間。在兩階段提交的時候,有一張流程圖。

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

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

呼叫fsync持久化。

mysql為了讓組提交的效果更好,把redo log做fsync的時間拖到了步驟1之後。也就是說,上面的圖變成了這樣:

這麼一來,binlog也可以組提交了。在執行圖5中第4步把binlog fsync到磁碟時,如果有多個事務的binlog已經寫完了,也是一起持久化的,這樣也可以減少iops的消耗。

不過通常情況下第3步執行得會很快,所以binlog的write和fsync間的間隔時間短,導致能集合到一起持久化的binlog比較少,因此binlog的組提交的效果通常不如redo log的效果那麼好。

如果想提公升binlog組提交的效果,可以通過設定 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count來實現。

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

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

這兩個條件是或的關係,也就是說只要有乙個滿足條件就會呼叫fsync。當binlog_group_commit_sync_delay設定為0的時候,binlog_group_commit_sync_no_delay_count也無效了。

wal機制主要得益於兩個方面:

redo log 和 binlog都是順序寫,磁碟的順序寫比隨機寫速度要快;

組提交機制,可以大幅度降低磁碟的iops消耗。

kafka 資料不丟失

設定引數 props.put bootstrap.servers 10.176.2.170 9092,10.176.1.97 9092,10.176.7.57 9092 producer用於壓縮資料的壓縮型別。預設是無壓縮 props.put compression.type gzip 增加延遲 p...

資料丟失後注意保護現場

最近做了一次 塊硬碟壞兩塊的raid 陣列資料恢復,準確來說是用去六天時間才將資料完整恢復。在此,向朋友提個小小的建議 資料恢復沒有絕對的成功,也沒有絕對的失敗,關鍵在於資料丟失時你的第一選擇。所以,在資料發生問題的第一時間保護好現場,不再發生二次破壞是最大程度挽救資料的前提條件。誤分割槽後的現場保...

storm(08) storm訊息不丟失機制

通過ack機制,spout傳送出去的每一條訊息,都可以確定是被成功處理或失敗處理,從而可以讓開發者採取動作。比如在meta中,成功被處理,即可更新偏移量,當失敗時,重 送資料。因此,通過ack機制,很容易做到保證所有資料均被處理,保證訊息不丟失。另外需要注意的,當spout觸發fail動作時,不會自...