日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

LSN(Log Sequence Number,日志邏輯序列號)是單調遞增的,用來對應redo log的一個個寫入點,每次寫入長度為length的redo log,LSN的值就會加上length。

周杰倫的所有歌曲中,我最喜歡的歌就是《聽媽媽的話》,其中有這么一句歌詞:小朋友,你是否有很多問號,為什么別人在那看漫畫,我卻在學畫畫。

應用在現在的場景就是:小伙伴,你是否有很多問號,為什么別人只需要簡單用一下MySQL,你卻要對MySQL深入淺出。

實際上每天的進步都是為了自己能接受頂尖大佬的技術熏陶,雖然我們不能親自聆聽他們的聲音,但是他們已經將自己的思路寫在了他們的開源項目里,這就是我們學習開源項目的意義所在。

比如今天,我們的話題是:MySQL可以存儲上億級別的數據,但是卻幾乎不會丟失數據,這里面到底是因為什么?

先給出結論:MySQL的數據不丟失就需要保證binlog和redo log都持久化到磁盤,因此,為了保證數據不丟失,就需要了解兩個日志的寫入機制。

1 binlog寫入機制

1.1 寫入原則

binlog的寫入邏輯為:事務執行過程中,先把日志寫到binlog cache,事務提交的時候,再把binlog cache寫到binlog文件中。

同時,一個事務的Binlog是不能拆開的,因此,無論事務多大,也要確保一次性寫入,這就涉及到binlog cache的保存問題。原因在于:binlog寫入的前提條件是事務被提交,事務至少進入prepare狀態,若此時一個事務的binlog拆分寫,意味著備庫執行時,可能將還沒有提交的事務執行,導致主備數據不一致。

系統給binlog cache分配了一塊內存,每個線程一個,參數binlog_cache_size用于控制單個線程內binlog cache所占內存大小。如果超過了這個參數規定大小,就要暫存到磁盤。可以通過語句show status like 'Binlog_cache_disk_use';判斷默認大小32KB是否滿足大小,如果語句的值遠大于0,需要增加binlog_cache_size的值;

MySQL:億級別數據不丟失是如何實現的圖片

事務提交時,執行器把binlog cache里的完整事務寫入到binlog,并清空binlog cache。實際上,在第一段提交狀態變為prepare狀態時,就可以把binlog cache寫入binlog,因此,即使之后crash,也能恢復數據。

1.2 寫入流程

MySQL:億級別數據不丟失是如何實現的圖片

如圖所示,每個線程有自己binlog cache,但是共用同一份binlog文件。執行流程為:

  • 事務執行過程中先把日志寫到binlog cache ,事務提交的時候再把binlog cache 寫入到binlog文件中,并清空binlog cache;
  • 系統為每個線程分配了一片binlog cache內存,參數binlog_cache_size控制單個線程內binlog cache大小。如果超過這個大小就要暫存到磁盤;
  • 事務提交的時候,執行器把binlog cache里完整的事務寫入binlog中。并清空binlog cache。 
  • 每個線程都有自己的binlog cache,共用一份binlog文件 
  • write,是把日志寫入到文件系統的page cache內存中,沒有持久化到磁盤,所以速度比較快。fsync是將數據持久化到磁盤,因此說,fsync才會占用磁盤的IOPS;

Page Cache是OS關于磁盤IO的緩存,位于內核中,不適用于大文件傳輸,因為大文件傳輸page cache的命中率比較低,這個時候page cache不僅沒有起到作用還增加了一次數據從磁盤buffer到內核page cache的開銷;

高版本的linux系統中已經把Buffer跟虛擬文件系統的page cache合并在一起了,因此也就沒有從磁盤buffer拷貝到內核page cache的開銷;

write和fsync的時機,由參數sync_binlog控制(與redis的Appendfsync相似):

  • sync_binlog=0,每次提交事務都只write,不做fysnc;
  • sync_binlog=1 的時候,表示每次提交事務都會執行 fsync;
  • sync_binlog=N(N>1) 的時候,表示每次提交事務都 write,但累積 N 個事務后才 fsync。

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

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

2 redo log機制

2.1 redo log三種狀態

MySQL:億級別數據不丟失是如何實現的圖片

如圖所示的三種顏色就是redo log的三種狀態:

  • 紅色部分:存在redo log buffer中,物理上是在MySQL進程內存中;
  • 黃色部分:寫到磁盤(write),但是沒有持久化(fsync),物理上是在文件系統的page cache中;
  • 綠色部分:持久化到磁盤,對應的是hard disk;

fsync函數同步內存中所有已修改的文件數據到儲存設備。一般情況下,對硬盤(或者其他持久存儲設備)文件的write操作,更新的只是內存中的頁緩存(page cache),而臟頁面不會立即更新到硬盤中,而是由操作系統統一調度,如由專門的flusher內核線程在滿足一定條件時(如一定時間間隔、內存中的臟頁達到一定比例)內將臟頁面同步到硬盤上(放入設備的IO請求隊列)。 因為write調用不會等到硬盤IO完成之后才返回,因此如果OS在write調用之后、硬盤同步之前崩潰,則數據可能丟失。

如果事務執行過程中MySQL發生異常重啟,這部分日志丟了,也不會有損失,因為事務還沒有提交, 因此,redo log buffer不需要每次生成都直接持久化磁盤。

2.2 redo log寫入策略

由于都是內存操作,因此日志寫入redo log buffer,以及write到page cache都很快,但是持久化磁盤的速度比較慢。

為控制寫入策略,InnoDB提供了innodb_flush_log_at_trx_commit參數:

  • 0:每次事務提交都只是把redo log留在redo log buffer;
  • 1:每次事務提交都將redo log直接持久化到磁盤;【innodb的默認值】
  • 2:每次事務提交時都只是把redo log寫到page cache;

2.3 刷盤時機

1)定時任務:InnoDB有一個后臺線程,每隔1秒,就會把redo log buffer日志調用write寫入到文件系統的page cache,然后調用fsync持久化到磁盤。

事務執行過程中的redo log也是直接寫入到buffer中,這些redo log也會被后臺線程一起持久化到磁盤,因此,一個沒有提交的事務的redo log也可能已經持久化到磁盤。

2)空間不足:redo log buffer占用的空間即將到達innodb_log_buffer_size一半時,后臺線程會主動寫盤。注意,此時由于這個事務還沒有提交,所以這個寫盤動作只是write,沒有調用fsync,即:只是寫入到page cache中。

MySQL:億級別數據不丟失是如何實現的圖片

3)其他事務提交:并行事務提交時,順帶將這個事務的redo log buffer持久化到磁盤。假設一個事務 A 執行到一半,已經寫了一些 redo log 到 buffer 中,這時候有另外一個線程的事務 B 提交,如果 innodb_flush_log_at_trx_commit 設置的是 1,那么按照這個參數的邏輯,事務 B 要把 redo log buffer 里的日志全部持久化到磁盤。這時候,就會帶上事務 A 在 redo log buffer 里的日志一起持久化到磁盤。

2.4 配置說明

兩階段提交,時序上是redo log先prepare,再寫binlog,最后再把redo log commit。

  • 在redo log執行prepare階段MySQL異常重啟,redo log沒有fsync,內存丟失,直接回滾,不影響數據一致性;
  • 當redo log執行fsync成功,但是binlog持久化異常,此時MySQL異常重啟,此時檢查redo log在prepare狀態,但是Binlog寫入失敗,則直接回滾即可;
  • 當binlog持久化后,但是redo log commit失敗,此時的redo log一定是prepare狀態,并且binlog完成,則添加commit標記,進而提交執行持久化,滿足數據一致性;
  • binlog完成且提交,redo log也commit成功,此時數據滿足一致性。

如果把innodb_flush_log_at_trx_commit設置為1,那么redo log在prepare階段就要持久化一次,因為crash-safe依賴于prepare狀態的redo log + binlog恢復。

每秒一次后臺輪詢刷盤,再加上crash-safe,InnoDB認為redo log在commit時只需要write到文件系統的page cache就可以了,因為只要binlog寫盤成功,就算redo log狀態還是prepare狀態也會被認為事務已經執行成功,所以只需要write到page cache就OK了,沒必要浪費IO主動執行一次fsync。

  • redo log prepare && binlog commit:事務提交;
  • redo log prepare && binlog uncommitted:事務回滾;

MySQL的“雙1”配置,指的就是sync_binlog和innodb_flush_log_at_trx_commit設置為1.即:一個事務完整提交前,需要等待兩次刷盤,一次是redo log的prepare階段,一個是Binlog。

這里需要注意的是,在事務中有兩次commit,第一次commit是事務語句的commit,這里說的commit主要是第二次commit,即:redo log的commit。

兩個commit的不是一個東西,在事務提交時,commit語句可以稱為commit1, 這時就會將redolog和 binlog fsync到磁盤, 這里寫入的redolog是prepare狀態(此時是語句的commit事務),如果這個prepare狀態的redolog和binlog都fsync成功的話,這個數據就不會丟失了。 然后后續把redolog的狀態從prepare的狀態變成commit狀態,這里稱為commit2,這里的改變狀態是后臺線程刷的,和數據不丟就沒啥關系,只是為了讓redolog狀態完整。

2.5 組提交(group commit)

2.5.1 LSN

LSN(Log Sequence Number,日志邏輯序列號)是單調遞增的,用來對應redo log的一個個寫入點,每次寫入長度為length的redo log,LSN的值就會加上length。

LSN可以看成是事務提交的序號,這個序號是在事務提交寫盤的時候生成的,因此可以說LSN反映了事務提交的順序。

LSN也會寫到InnoDB的數據頁中,確保數據頁中不會被多次執行重復的redo log。

MySQL:億級別數據不丟失是如何實現的圖片

如圖所示三個并發事務 (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可以直接返回;

MySQL當多個線程在提交完prepare,redo log寫入到redo log buffer中,此時,redo log buffer存在多個線程的日志,并同步更新了LSN。第一個寫完的線程帶著LSN去刷盤,寫完后,別的線程發現自己的redo log已經寫完了(LSN大于線程的LSN),直接就返回。

如上所示,一個組提交的事務越多,節約磁盤的IOPS效果越好。在并發場景,即使innodb_flush_log_at_trx_commit設置為1,事務每次prepare都要執行刷盤,此時可能有其他的線程也在執行事務,也可以將他們組成一個組實現組提交。

2.5.2 兩階段優化

MySQL:億級別數據不丟失是如何實現的圖片

兩階段提交可以簡化為如下兩步:

  • 先把binlog從binlog cache寫到磁盤的binlog文件;
  • 調用fsync持久化;

既然組提交能夠優化磁盤的IOPS,那就有了如下的優化:

MySQL:億級別數據不丟失是如何實現的圖片

如圖所示,把redo log做fysnc的時間拖到了步驟1之后,采用交叉fsync的方式,就是為了收集更多的“提交”,這樣的組提交效果更好一些。

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

不過通常情況下第 3 步執行得會很快(redo log順序寫,相對較快),所以 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消耗;

3 MySQL的IO瓶頸優化

分析到這里,我們再來回答這個問題:如果你的 MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?針對這個問題,可以考慮以下三種方法:

  • 設置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 參數,減少 binlog 的寫盤次數。這個方法是基于“額外的故意等待”來實現的,因此可能會增加語句的響應時間,但沒有丟失數據的風險。之所以說沒有丟失數據風險,指的是無論fsync延遲多久,只要binlog沒有持久化,只是做回滾,不會出現丟數據。但是可能導致業務側超時。
  • 將 sync_binlog 設置為大于 1 的值(比較常見是 100~1000)。這樣做的風險是,主機掉電時會丟 binlog 日志。
  • 將 innodb_flush_log_at_trx_commit 設置為 2。這樣做的風險是,主機掉電的時候會丟數據。

但是并不建議把 innodb_flush_log_at_trx_commit 設置成 0。因為把這個參數設置成 0,表示 redo log 只保存在內存中,這樣的話 MySQL 本身異常重啟也會丟數據,風險太大。而 redo log 寫到文件系統的 page cache 的速度也是很快的,所以將這個參數設置成 2 跟設置成 0 其實性能差不多,但這樣做 MySQL 異常重啟時就不會丟數據了,相比之下風險會更小。

分享到:
標簽:MySQL
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定