iT邦幫忙

2026 iThome 鐵人賽

DAY 28
0
Software Development

一套真實運作中的 Laravel 系統,拆解它的原生機制系列 第 28 篇

Day 28:一次資料遷移——dry-run/chunk/三種結果統計,安全搬歷史資料的範本

  • 分享至 

  • xImage
  •  

前言:「換一套路徑規則」聽起來簡單,「搬歷史資料」才是真正的挑戰

昨天看到一個只有十幾行、把檔案路徑換成人類看得懂的結構的類別。但這裡有一個沒有講完的問題:如果系統已經上線一段時間,累積了大量用舊路徑規則儲存的檔案,換了新規則之後,這些舊檔案要怎麼辦?

新的程式碼會用新規則去讀取路徑,但檔案實際上還躺在舊路徑底下——不做點什麼,這些歷史檔案就會變成「資料庫裡有紀錄、但實際路徑對不上」的孤兒資料。今天要看的是一支專門處理這個問題的 Artisan command,它的設計細節,是任何要寫資料遷移工具的人都該參考的範本。

今日目標

  • 理解「換路徑規則」跟「搬歷史資料」是兩個獨立的問題,不能只解決前者
  • 拆解一支真實的資料遷移 command,看它怎麼降低風險
  • 學會三個資料遷移工具該有的設計元素:先看計畫、分批處理、誠實統計結果

本文主體

資料遷移最大的風險:一次性、大規模、不可逆

寫一支資料遷移工具,最危險的地方在於它通常是「一次性」執行、「大規模」影響資料、而且很多操作是不可逆的——尤其是牽涉到搬移或刪除實體檔案的情境,一旦搬錯,找不到原始檔案就真的找不回來了。

正因為風險這麼高,一支寫得好的遷移 command,不會是「一行迴圈把所有資料跑過一遍」這麼簡單,而是要在設計上主動降低犯錯的代價。

拆解真實案例:三個關鍵設計

這個系列的素材專案裡,有一支負責把媒體檔案從舊路徑結構搬到新路徑結構的 command,具備三個值得逐一拆解的設計。

第一,--dry-run:先看計畫,不動手。 這支 command 支援一個選項,開啟後只會印出「哪個檔案會被搬到哪裡」的完整清單,不會真的移動任何檔案。這讓操作者可以在正式執行前,先確認遷移計畫符合預期——如果 dry-run 的輸出裡出現不合理的路徑,代表遷移邏輯本身可能有問題,這時候還沒真的動到任何檔案,回頭修邏輯的代價是零。

第二,chunk() 分批處理。 command 沒有一次把所有媒體紀錄全部載入記憶體處理,而是用 Laravel 的 chunk() 方法,一次只處理一小批(一百筆),處理完這批才去讀下一批。這個設計在資料量大的時候特別重要:一次載入全部資料容易撐爆記憶體,而分批處理也讓整個遷移過程可以在中途觀察進度、甚至在必要時安全中斷。

第三,逐筆比對、統計三種結果,而不是只回報成功/失敗。 這是最容易被忽略、但其實最重要的設計。這支 command 對每一筆媒體紀錄,會先比對新舊路徑——如果新舊路徑其實一樣(代表這筆資料原本就已經符合新規則,不需要搬),計入「跳過」;如果來源檔案在磁碟上找不到(代表資料庫紀錄跟實際檔案系統已經不一致,這是遷移過程中才會揭露的既有問題),計入「找不到」,並且用警告訊息點名是哪一筆,而不是讓整個遷移中斷;只有真的需要搬、也真的搬成功的,才計入「已搬移」。

為什麼「三種結果」比「成功或失敗」更誠實

用一組對照來看第三點的價值:

❌ 只回報「處理了幾筆」「失敗了幾筆」
→ 「找不到來源檔案」跟「這筆資料本來就不用搬」
  被混在一起計算,操作者沒辦法從最終報告判斷
  「找不到」的那幾筆是不是資料本身已經有問題,
  只能自己再翻一次 log 才知道

✅ 拆成「已搬移」「跳過」「找不到」三種統計
→ 執行完直接看報告就知道:有多少筆是正常遷移完成、
  有多少筆本來就不需要動、有多少筆代表資料庫跟
  實際檔案系統已經不一致——這第三類數字,
  往往是遷移過程額外揭露出來的既有問題,
  值得獨立追蹤,不該被混進「失敗」裡草草帶過

「找不到來源檔案」不必然是這次遷移造成的問題,很可能是更早之前就存在的資料完整性缺口,只是沒有人去檢查過。用一支遷移工具順便把這類既有問題攤開來看,是這個設計額外帶來的價值。

遇到單筆問題,不中斷整個流程

另一個值得注意的細節:遇到找不到來源檔案這種情況,command 選擇用 $this->warn() 印出警告訊息、記一筆統計,然後繼續處理下一筆,而不是直接拋出例外中斷整個遷移。

這個選擇背後的判斷是:資料遷移工具處理的往往是幾百、幾千筆資料,如果因為其中一筆有問題就讓整個流程中斷,操作者要嘛得手動排除掉那筆問題資料才能重跑,要嘛每次中斷都要從頭排查——把單筆的問題留下記錄、讓其他健康的資料正常完成遷移,事後再統一處理那份「找不到」的清單,是效率跟穩健性都更好的做法。

今日思考題

如果你要寫一支資料遷移工具,除了「dry-run」跟「分批處理」,你會怎麼設計統計報告的分類?回想你維護過的系統,有沒有類似「資料庫紀錄跟實際狀態不一致」的既有問題,是可以透過一次遷移工具順便揭露出來的?

今日重點回顧

  • 資料遷移的風險在於一次性、大規模、往往不可逆,設計上要主動降低犯錯代價
  • 三個關鍵設計:--dry-run 先看計畫不動手、chunk() 分批處理避免撐爆記憶體、統計拆成三種結果而不是只有成功/失敗
  • 「找不到來源檔案」不等於「這次遷移失敗」,很可能是遷移過程額外揭露出來的既有資料完整性問題,值得獨立追蹤
  • 單筆資料有問題時記錄下來、繼續處理其他資料,比讓整個流程中斷更務實

明日預告

明天要看一個套件能力邊界的案例:一個原生只處理 Model CRUD 異動的稽核套件,怎麼被延伸去涵蓋「登入」這種不屬於 Model CRUD 的事件。


上一篇
Day 27:一套路由、兩套畫面——用 View Finder 切換主題,而不是複製一份路由
系列文
一套真實運作中的 Laravel 系統,拆解它的原生機制 共 28 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言