大多數用戶在對於磁盤進行分區的時候都是習慣性的不給系統盤預留很大空間,其實這並不是一個好習慣。因為系統分區並不像我們想象的那樣會僅僅安裝一個操作系統,系統分區多數還是會承載操作系統主要應用軟件安裝任務。那么當磁盤空間爆滿后,MySQL會發生什么事呢?又應該怎么應對?
會發生什么事
當磁盤空間寫滿了之后,MySQL是無法再寫入任何數據的,包括對表數據的寫入,以及binlog、binlog-index等文件。
當然了,因為InnoDB是可以把臟數據先放在內存里,所以不會立刻表現出來無法寫入,除非開啟了binlog,寫入請求才會被阻塞。
當MySQL檢測到磁盤空間滿了,它會怎么樣呢?下面我們來看一個具體例子:
磁盤滿了之后MySQL會做什么?
我們看下官方的說法
其實MySQL本身並不會做任何操作,如官方文檔說說,只會每分鍾check一次是否有空閑空間,並且10分鍾寫一次錯誤日志。
但是再次期間由於磁盤滿了,意味着binlog無法更新,redolog也無法更新,所有bufferpool中的數據無法被flush上,如果不幸的服務器重啟,或者實例被kill了,那必然會造成數據丟失,這幾乎是一定的。所以,處理磁盤滿的問題最好是先釋放出來一定空間讓dirty數據刷新下來。
磁盤滿了為什么會導致操作hang住?
1、select
首先經過經驗和實際測試,select操作不會由於磁盤滿導致問題,也就是所有select操作都會正常運行。
2、insert
經過不通的測試發現,當磁盤滿了之后,並不是第一個insert就卡住,而是會在n個之后出現卡住的情況。
通過查看error日志,發現卡住現象和刷磁盤的操作有關系。
為了驗證推論是否正確,我們將sync_binlog設置為1,在這種情況下,insert第一條就卡住了,並且errorlog中直接報錯提示寫binlog失敗。看來卡住確實和刷磁盤有關系。
目前已知和刷磁盤有關系的參數有3個,分別是sync_binlog,innodb_flush_log_tr_commit,和duoblewrite。
3、showslavestatus
在從庫經過測試,操作會被卡住,這主要是由於執行showslavestatus需要獲得LOCK_active_mi鎖,然后鎖上mi->data_lock,但是由於磁盤滿了無法將io_thread中的數據寫入到relaylog中,導致io_thread持有mi->data_lock鎖,這就導致了死鎖。
所以,這就導致在磁盤滿的情況下,執行showslavestatus操作會卡住。
4、showstatus
測試可以正常操作,但是如果先執行了showslavestatus操作的情況下,showstatus也會被卡住。這是因為執行showstatus需要鎖上LOCK_status,而由於status狀態中包含slavestatus,所以還需要鎖上LOCK_active_mi。如果限制性了showslavestatus,這時候由於mi->data_lock死鎖問題,導致io_thread不會釋放LOCK_active_mi鎖。這時候就導致showstatus和showslavestatus爭搶同一把LOCK_active_mi鎖,也形成了死鎖。
所以,在磁盤滿的情況下,如果先執行showslavestatus,后執行showstatus,連個操作都會卡住。
應該怎么辦
那么,當發現磁盤空間滿了之后,我們應該怎么處理呢,建議:
每分鍾:檢查空間是否得到釋放,以便寫入新數據。當發現有剩余空間了,就會繼續寫入數據,一切照舊。
每十分鍾:如果還是發現沒剩余空間,則會在日志中寫入一條記錄,報告磁盤空間滿(這時候只寫入幾個字節還是夠的)。
提高監控系統檢測頻率,預防再次發生;
及時刪除不用的文件,釋放空間;
若有線程因磁盤滿的問題被阻塞了,可先殺掉,等到下一分鍾重新檢測時它可能又可以正常工作了;
可能因磁盤滿導致某些線程被阻塞,引發其他線程也被阻塞,可把導致阻塞的線程殺掉,其他被阻塞的線程也就能繼續工作了。
例外
有個例外的情況是:
當執行REPAIRTABLE或者OPTIMIZETABLE操作時,或者執行完LOADDATAINFILE或ALTERTABLE之后批量更新索引時,這些操作會創建臨時文件,當執行這些操作過程中mysqld發現磁盤空間滿了,就會把這個涉及到的表標記為crashed,刪掉臨時文件(除了ALTERTABLE操作,MySQL會放棄正在執行的操作,刪除臨時文件,釋放磁盤空間)。
備注:當執行這些命令過程中mysqld進程被意外被殺掉的話,其所生成臨時文件不會自動刪除,需要手工刪掉才能釋放磁盤空間。