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