十六、mysql中如何保證數據的可靠性

目錄

MySQL寫入binlog 和redo log 的流程

binlog 的寫入機制

redo log 的寫入機制

如果MySQL出現了性能瓶頸,而且瓶頸在IO上,可以通過哪些方法來提升性能?


只要redo log 和binlog 保證持久化到磁盤,就能確保MySQL異常重啓後,數據可以恢復。

MySQL寫入binlog 和redo log 的流程

binlog 的寫入機制

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

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

系統給binlog cache 分配了一片內存,每個線程一個,參數binlog_cache_size用於控制單個線程內binlog cache所佔內存的大小。如果超過了這個參數規定的大小,就要暫存到磁盤。事物提交的時候,執行器把binlog cache裏的完整事物寫入到binlog 中,並清空binlog cache。

 

binlog寫盤狀態

可以看到,每個線程都有自己的binlog cache,但是共用同一份binlog文件。

途中的write,指的就是把日誌寫入到文件系統的page cache,並沒有把數據持久化到磁盤,所以速度比較快;

圖中的fsync,纔是將數據持久化到磁盤的操作,一般情況下,fsync才佔磁盤的IOPS。

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

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

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

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

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

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

 


redo log 的寫入機制

事物在執行過程中,生成的redo log是要先寫到redo log buffer的。redo log buffer裏面的內容並非每次生成後都要直接持久化到磁盤的。

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

事物還沒提交的時候,redo log buffer中的部分日誌也有可能被持久化到磁盤。這個問題需要從redo log 可能存在的三種狀態說起:

1、存在redo log buffer中,物理上是在MySQL進程內存中;

2、寫到磁盤(write),但是沒有持久化(fsync),物理上是在文件系統的page cache 裏面;

3、持久化到磁盤,對應的是hard disk。

日誌寫到redo log buffer是很快的,write到page cache也差不多,但是持久化到磁盤的速度就慢多了。爲了控制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。

InnoDB有一個後臺線程,每隔1秒,就會把redo log buffer中的日誌,調用write 寫到文件系統的page cache,然後調用fsync 持久化到磁盤。

注意:事物執行中間過程的redo log 也是直接寫在redo log buffer 中的,這些redo log也會被後臺線程一起持久化到磁盤。也就是說,一個沒有提交的食物的redo log,也是可能已經持久化到磁盤的。實際上,除了後臺線程每秒一次的輪詢操作外,還有兩種場景會讓一個沒有提交的食物的redo log 寫入到磁盤中:

1、redo log buffer佔用的空間即將達到innodb_log_buffer_size一半的時候,後臺線程會主動寫入到磁盤中。由於這個事物並沒有提交,所以這個寫盤動作只是write,而沒有調用fsync,也就是隻留在了文件系統的page cache。

2、並行的事物提交的時候,順帶將這個事物的redo log buffer持久化到磁盤。

兩階段提交,時序上redo log先prepare,再寫binlog,最後再把redo log commit.


如果MySQL出現了性能瓶頸,而且瓶頸在IO上,可以通過哪些方法來提升性能?

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

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.這樣做的風險是,主機掉電的時候會丟失數據。

 

 


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章