MapleCheng

在浩瀚的網路世界中無限潛水欸少年郎!

0%

資料刪掉了,磁碟為什麼還是滿的?

最近處理一個內部服務的儲存空間問題時,我又碰到一個很典型、也很容易讓人誤判的場景:資料庫裡大量紀錄已經刪掉,查詢結果也確認不存在了,但磁碟空間幾乎沒有回來。

從應用程式看,清理成功;從作業系統看,什麼都沒發生。兩邊都沒有說謊,只是各自在描述不同層次的事。這也提醒我,資料生命週期如果只管「紀錄還在不在」,其實只做了一半。真正完整的清理,還要一路管到儲存引擎如何配置檔案、空間何時歸還,以及系統能不能在重建後正確恢復。

刪除紀錄,不等於歸還磁碟

很多應用團隊對資料庫的直覺,來自檔案系統:檔案刪掉,空間就應該釋放。資料庫通常不是這樣運作。

以常見的儲存引擎來說,資料被刪除後,頁面可能只是被標記為可重用。這些空間可以留給同一個資料庫未來寫入,但底層資料檔不一定縮小,作業系統自然也不會立刻看到更多可用容量。這是合理的設計,因為頻繁縮放檔案會帶來額外 I/O、碎片與效能抖動;對持續成長的工作負載而言,保留已配置空間通常比反覆交還更有效率。

問題出在兩種「可用空間」被混在一起了。

第一種是資料庫內部可重用的空間;第二種是作業系統可以分配給其他服務的空間。當主機磁碟快滿、容器 volume 持續膨脹,或資料庫要縮編時,我們需要的是第二種。只看 collection 筆數、邏輯資料量或刪除指令成功與否,無法證明實體容量真的回來。

因此,「我已經把舊資料刪了」不能當成維運工作的完成條件。更精確的問題應該是:資料庫內還有多少有效資料?資料檔實際占多少?volume 占多少?主機剩多少?刪除後的空間是留在引擎內重用,還是已經交還作業系統?

清理之前,先決定什麼才是真相

這類事故真正困難的地方,往往不是下刪除指令,而是判斷哪些資料可以消失。

一個運作一段時間的系統,常會同時累積交易紀錄、快取、分析結果、排程執行歷史、暫存報表、觀測日誌與各種為了除錯留下的快照。它們可能都放在同一套資料庫裡,卻有完全不同的保存價值。若只用「最近沒查到」判斷,很容易把冷資料誤當垃圾,也可能把其實能重算的衍生資料保留多年。

我現在會先把資料分成幾類:

  • 權威資料:無法從其他來源重建,必須備份與驗證。
  • 衍生資料:可由權威資料重新計算,必要時可以丟棄。
  • 快取與暫存:有明確失效條件,應自動淘汰。
  • 稽核與追溯資料:依風險、法規與營運需求設定保存期。
  • 除錯產物:預設短期保存,不應無限成長。

這個分類比「哪張表最大」重要。沒有先定義 source of truth,清理就只是對著容量壓力做猜測;運氣好是省到空間,運氣不好則是在事故中製造另一場事故。

同樣地,備份也不能只做整庫打包。若真正需要保留的只有少量權威資料,就應該把備份範圍、還原順序與驗證方式寫清楚。備份檔存在,不代表它可用;只有實際確認能讀取、筆數與關鍵欄位正確,才算有還原能力。

有時候,重建比原地壓縮更乾淨

當大量歷史資料已刪除,但底層檔案仍然龐大時,通常會面臨幾種選擇:等待空間被未來寫入重用、執行 compact、做 resync,或將必要資料匯出後重建儲存空間。

沒有一個答案適合所有環境。原地 compact 可能需要額外暫存容量,也可能鎖住資源或帶來長時間 I/O;等待重用則無法解決主機已經接近滿載的問題。若資料量已大幅縮小,而且停機窗口、備份與還原路徑都可控,重建 volume 反而常是最容易理解、也最能確定結果的做法。

但「刪掉 volume 再建一個」絕對不該是臨場反應。它應該被拆成一條可驗證流程:

  1. 盤點並凍結權威資料的寫入範圍。
  2. 匯出備份,並在刪除前確認備份可讀。
  3. 記錄關鍵集合、筆數、索引與服務設定。
  4. 停止會持續寫入的應用程式與排程。
  5. 重建儲存空間並還原必要資料。
  6. 驗證資料庫角色、健康狀態、索引與查詢結果。
  7. 啟動應用程式,執行端到端 smoke test。
  8. 比較資料庫、volume 與主機三層容量,確認空間真的回收。

這套流程的重點不是命令,而是每一步都有「可以停下來檢查」的證據。尤其在刪除舊 volume 前,備份可讀與還原條件必須先成立;重建後,也不能只看到 container healthy 就宣布完成。資料庫健康、應用程式能查到正確資料、排程不會立刻把垃圾灌回來,才算真的恢復。

容量治理不能等到磁碟告警

事後回頭看,磁碟占滿通常不是單一 bug,而是資料生命週期缺了一截。系統知道怎麼寫入,卻不知道資料何時過期;知道怎麼刪除紀錄,卻不知道儲存引擎是否歸還空間;有備份,卻沒有定期還原演練;有容量告警,卻沒有依成長速度估算還剩多少時間。

因此,我會把容量治理至少做成四件事。

第一,為每類非權威資料設定 TTL、保留期或批次清理規則,別讓人工整理成為唯一出口。第二,同時監控邏輯資料量、資料檔大小、volume 使用量與主機容量,避免只看單一層。第三,對異常成長設定趨勢告警;剩餘 20% 只是結果,過去一週每天增加多少,往往更能提早發現問題。第四,把 compact、重建與還原演練寫進 runbook,並說清楚何時使用、需要多少額外空間、可接受多少停機時間。

如果服務跑在容器裡,還要特別注意:刪除 collection 不代表 Docker volume 變小;重建資料庫也不代表應用服務自動拿回正確連線、權限與啟動順序。資料層、容器層和主機層是三個不同的觀測面,缺一個都可能得到「看起來好了」的假象。

真正的完成,是能證明資料與空間都對

這次經驗讓我再次確認,維運裡最危險的字之一就是「已刪除」。它只說明某個動作執行過,沒有說明資料是否安全、容量是否回收、服務是否恢復,也沒有說明同樣的問題會不會再發生。

身為技術主管,我更在意的是一條完整證據鏈:保留的資料有清楚的權威來源,刪除的資料有保存政策,備份已驗證可還原,重建後查詢與服務正常,實體容量確實下降,而且未來有自動淘汰與成長告警。

資料生命週期不是從 create 到 delete,而是從建立、使用、保存、淘汰,一直到實體空間回收與還原能力被驗證。把最後這一段補上,清理才不只是暫時止血,而是系統真的恢復到可持續運作的狀態。