第一章:MySQL的“假刪除”困局
深夜11點,運維工程師小明收到數據庫服務器磁盤告警:空間使用率95%!分析發現BIG_TABLE表的2年前日志是元兇。他果斷執行清理:
DELETE FROM BIG_TABLE WHERE create_time < '2023-01-01'; -- 刪除12億條歷史日志
確認事務提交后,監控顯示表數據已消失。但次日清晨,服務器磁盤爆滿,數據庫陷入癱瘓——空間竟絲毫未釋放!
分析:
InnoDB的“標記刪除”機制下,DELETE操作僅將數據標記為邏輯刪除(打上刪除標簽),物理數據仍占據磁盤。目的是支持MVCC(多版本并發控制),保證事務隔離性。
在空間歸屬權上,InnoDB不會主動將空間歸還OS,僅允許新數據復用原有空間。
釋放方案:
-- 徹底釋放空間
OPTIMIZE TABLE BIG_TABLE; -- 重建表消除碎片
第二章:Linux的“僵尸文件”之謎
一周后,小明再遇磁盤告警!Apache日志文件access.log已增長至40GB。他火速執行:
bash
rm /var/log/apache/access.log # 刪除日志文件
ls -l /var/log/apache/ # 確認文件消失
然而兩小時后,業務系統崩潰——磁盤空間依然耗盡!
分析:
inode引用計數器:
rm僅刪除文件路徑(目錄項),inode引用數減1
Apache進程仍持有文件句柄,引用計數>0。
空間持有者實錘:
lsof | grep deleted # 關鍵命令揪出"僵尸文件"
apache2 14836 root 1w REG 254,0 42G 65536 /var/log/apache/access.log (deleted)
如果一個進程在 rm 之前就已經打開了文件 A,那么它手里就握著一個指向該文件 inode 的文件描述符(File Descriptor)。 內核會為所有打開的文件維護一個引用計數器。只要還有進程持有文件描述符,即使目錄中的鏈接已經被刪除(link count = 0),內核也不會立即釋放該文件的數據塊,因為進程可能還要進行讀寫操作。此時,這個文件就變成了一個“你看不到它,但它卻真實存在”的文件。這種設計是非常合理和安全的,1、保證進程穩定性:確保正在使用文件的程序不會突然因為文件被刪除而崩潰或出錯。2、允許文件無縫替換:例如,日志文件可以被 rm 掉然后重新創建,而正在寫入的進程可以毫不知情地繼續向原來的文件描述符寫入,新的數據會寫到新創建的文件中
釋放方案:
# 方案1:優雅重啟釋放句柄 ,重啟進程
kill -HUP 14836 # 通知Apache重建日志文件([網頁12])
# 方案2:清空文件立即釋放空間,進程不能重啟的情況下的選擇
echo "" > /proc/14836/fd/1 # 通過進程文件描述符清空
最終啟示錄
刪除 ≠ 釋放!
在計算機的世界里,可見的刪除只是表象,空間的釋放才是終極奧義。無論是數據庫還是操作系統,都有必要了解其刪除和釋放的技術原理。經驗不足的同學,執行完刪除操作可能就完事了,較有經驗的同學會DOUBLE CHECK,確保刪除真正生效。但經驗豐富的同學,會用du命令檢查空間的釋放,提前發現問題。