MySQL是如何保證資料不丢失的?
- binlog的寫入機制
- redo log的寫入機制
隻要redo log和binlog能夠持久化到磁盤中,就能確定MySQL在異常重新開機後,資料可以恢複。下面一起來了解下MySQL中binlog和redo log的寫入流程。
binlog的寫入機制
事務執行過程中,先把日志寫到binlog cache,事務送出的時候,再把binlog cache寫到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控制的:
- sync_binlog=0的時候,每次送出事務都隻write,不fsync;
- sync_binlog=1的時候,每次送出事務都隻會執行fsync;
- sync_binlog=N(N>1)的時候,每次送出事務都write,但累計N個事務後才fsnc;
sync_binlgo設定為N的風險是:如果主機發生異常重新開機,會丢失最近N個事務的binlog日志;
redo log的寫入機制
redo log存在三種狀态
- 存在redo log buffer中,實體上是在MySQL程序記憶體中,就是圖中的紅色部分;
- 寫到磁盤(write),但是沒有持久化(fsync),實體上是在檔案系統的page cache裡面,也就是圖中的黃色部分;
- 持久化到磁盤,對應的是hard disk。也就是圖中的綠色部分;
為了控制 redo log 的寫入政策,InnoDB 提供了 innodb_flush_log_at_trx_commit 參
數,它有三種可能取值:
- 設定為 0 的時候,表示每次事務送出時都隻是把 redo log 留在 redo log buffer 中 ;
- 設定為 1 的時候,表示每次事務送出時都将 redo log 直接持久化到磁盤;
- 設定為 2 的時候,表示每次事務送出時都隻是把 redo log 寫到 page cache。
InnoDB有一個背景線程,每隔一秒,就會把redo log buffer中的日志,調用write寫到檔案系統的pagecache中,然後調用fsync持久化到磁盤中;
實際上,除了背景線程每秒一次的輪詢操作外,還有兩種場景會讓一個沒有送出的事務的redo log 寫入到磁盤:
-
redo log buffer 占用的空間即将達到 innodb_log_buffer_size 一半的時
候,背景線程會主動寫盤;
-
并行的事務送出的時候,順帶将這個事務的 redo log buffer 持久化到磁
盤;
在兩階段送出的時候說過,時序上redo log先prepare,再寫binlog,最後再把redo log commit;
上面的"寫binlog"是分成兩步的:
- 先把binlog從binlog cache中寫到磁盤上的binlog檔案;
- 調用fsync持久化;
如果你的 MySQL 現在出現了性能瓶頸,而且瓶頸在 IO 上,可以通過哪些方法來提升性能呢?
- 設定 binlog_group_commit_sync_delay 和binlog_group_commit_sync_no_delay_count 參數,減少 binlog 的寫盤次數;基于“額外的故意等待”,雖然增加了語句的響應時間,但沒有丢失資料的風險;
- 将sync_binlog設定為大于1的值,常見範圍是100~1000,風險是主機停電時會丢失binlog日志;
-
将 innodb_flush_log_at_trx_commit 設定為 2。這樣做的風險是,主機掉電的時候會
丢資料;