目錄
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動作時,不會自...