277
技術社區[雲棲]
【MySQL】磁盤寫滿之後,數據庫show status受到阻塞的原因
編輯手記:前兩天同事討論到一個問題,當mysql從庫磁盤滿之後,show status及show slave status會被卡住,但其他select操作不受影響,但如果數據庫是主庫,磁盤滿了之後,隻有dml會被阻塞,select及show是不會受影響的。於是一群人討論了一會,最後決定,SMC,以下就是我的結論。
1..以下所有討論都基於mysql 5.5.37版本及官方文檔,不保證適用於其他版本。
2.下文中提到的磁盤滿,指的是數據文件(數據文件,日誌文件,配置文件)所在磁盤分區。
3.由於篇幅問題,最後麵的代碼部分,隻有關鍵的函數及邏輯判斷部分。
首先查看官方文檔(https://dev.mysql.com/doc/refman/5.5/en/full-disk.html),有以下觀點:
1.實例每分鍾檢查是否有足夠的空間寫入。如果已經有空間了,繼續執行操作。
2.每十分鍾給日誌文件寫入一條記錄,報告磁盤已經寫滿。
但是對不對?
下麵是我對官方文檔的測試結果:
1.如果主庫上打開binlog,那麼當磁盤滿之後,每10分鍾,數據庫會報告一條Disk is full writing './mysql-bin.000001' (Errcode: 28). Waiting for someone to free space... (Expect up to 60 secs delay for server to continue after freeing disk space),也就是說bin log寫滿了,等待磁盤空間,這與文檔描述相同。
2,如果在主庫上關閉binlog,當磁盤滿了之後,任何插入行為都會失敗,報錯為[ERROR] ./mysqld: The table 'x' is full,官方文檔沒有提到這個情況,此處的表x是innodb表。
上麵是對主庫所在磁盤寫滿之後,數據庫實例的反應,下麵講講我們遇到的情況:從庫磁盤寫滿之後,show status及show slave status會被卡住,但其他select操作不受影響。
整個流程涉及3把鎖:
1.mi->data_lock
2.LOCK_active_mi
3.LOCK_status
當一個新操作被接收到slave io線程後,如果這時候磁盤寫滿了,這個寫入操作就會被阻塞,然後等待,直到磁盤有空間之後繼續寫入,這個操作中,會持有mi->data_lock鎖,隻有操作完成或者操作失敗後,這個鎖才會被釋放,恰好,磁盤滿不屬於錯誤,於是操作阻塞,該線程會一直持有mi->data_lock鎖。
當發起一個show slave status請求的時候,執行的時候,會首先鎖住LOCK_active_mi鎖,然後鎖定mi->data_lock鎖,當然,現在的情況下,mi->data_lock不會得到,於是LOCK_active_mi鎖就會被該線程持續持有。
另外其一個會話發起show global status,執行的時候首先鎖定LOCK_status鎖,由於show status包括,Slave_heartbeat_period,Slave_open_temp_tables,Slave_received_heartbeats ,Slave_retried_transactions這些狀態,於是還需要LOCK_active_mi鎖,於是,這個會話也會被阻塞掉。
之後如果再另外發起請求,由於LOCK_status已經被鎖定,於是所有涉及show status的請求,都會被阻塞到這裏。
之後所有show slave status請求也都會被阻塞在LOCK_active_mi鎖處。
看了以上的結論,是否會想到另外一個操作順序:磁盤寫滿->show status,這種操作的結果是:show status不會被阻塞的。
以下是mysql源代碼(5.5.37)涉及到的具體部分:
1.io線程阻塞的相關函數及部分代碼
slave.cc
slave.cc
log.cc
log.cc
mf_locache.c
my_write.c
errors.c
2.show slave status相關的函數及部分代碼
sql_parse.cc
sql_parse.cc
slave.cc
3.show status相關的函數及部分代碼
mysqld.cc
sql_show.cc
sql_show.cc
本文出自數據和雲公眾號,原文鏈接
最後更新:2017-07-18 10:32:58