Chapter 12: Managing Data#
引言#
資料(Data)及其管理為測試與部署流程帶來兩大獨特挑戰:
- 龐大的資料量:應用程式的狀態資料通常遠超原始碼與設定檔的體積
- 生命週期差異:應用程式資料必須被保留與遷移,資料往往比建立它的應用程式更長壽;部署新版本或回滾時,資料不能像程式碼一樣直接抹除重來
一旦系統上線,資料就會持續增長,並擁有獨立的價值——資料可以說是整個系統中最有價值的部分。因此,我們必須建立機制,讓資料結構或內容的變更能在最小化風險的前提下完成。其中最關鍵的做法就是自動化資料庫遷移流程(Automating Database Migration)。
本章主要以**關聯式資料庫(RDBMS)**為討論對象,但所提出的原則同樣適用於其他資料儲存系統(包含 NoSQL)。
資料庫腳本化(Database Scripting)#
所有資料庫的變更——初始化、遷移——都應被腳本化(Scripted)並存入版本控制(Version Control)。這些腳本應能服務於交付流程中的每一個環境:
- 開發者在本地建立新資料庫
- 系統整合測試(SIT)環境的升級
- 正式環境的資料庫遷移
資料庫的 Schema 會隨應用程式演進,因此必須確保部署到任一環境時,資料庫 Schema 能與對應版本的應用程式相匹配。
此外,資料庫腳本也應納入持續整合(CI)流程。驗收測試的前置作業應包含:以正確 Schema 建立資料庫,並載入必要的測試資料。
資料庫初始化(Initializing Databases)#
資料庫初始化是最簡單也最基本的起點。初始化腳本應依序執行:
- 建立資料庫結構——資料庫實例、Schema、使用者帳號等
- 載入參考資料——應用程式啟動所需的基礎資料
對於少數資料為暫時性或唯讀的簡單專案,每次部署時直接「抹除重建」即是最有效的策略。
如果你的場景允許每次部署都從零開始建立資料庫,那就直接這麼做——這是最簡單的策略。
漸進式變更(Incremental Change)#
對於大多數系統,部署時必須在保留現有資料的前提下更新資料庫。這包含三個面向:
- 持續整合要求每次變更後應用程式仍能正常運作(含資料庫變更)
- 持續交付要求任何成功的候選版本都能部署至正式環境
- 需要有**回滾策略(Rollback Strategy)**以應對部署失敗
資料庫版本控制(Versioning Your Database)#
最有效的自動化遷移機制是為資料庫加上版本號:
- 在資料庫中建立一張表記錄當前版本號
- 每次變更建立兩個腳本:
- 前滾腳本(Roll-Forward Script):從 version x 升級到 version x+1
- 回滾腳本(Roll-Back Script):從 version x+1 降回 version x
- 應用程式的設定中記錄其所需的資料庫版本
部署時,工具(如 DbDeploy)會比對當前資料庫版本與目標版本,自動依序執行對應的遷移腳本。前滾時由舊到新依序執行;回滾時由新到舊反向執行。
flowchart TD
A[檢查資料庫\n目前版本號] --> B{需要前滾?}
B -->|是| C[依序執行\n前滾腳本\n由舊到新]
B -->|否| D{需要回滾?}
D -->|是| E[依序執行\n回滾腳本\n由新到舊]
D -->|否| F[版本已是最新]
C --> G[更新版本號]
E --> G範例:初始腳本 1_create_initial_tables.sql 建立 customer 資料表;後續腳本 2_add_customer_date_of_birth.sql 包含 ALTER TABLE 語句及 --//@UNDO 標記後的回滾邏輯。
撰寫回滾腳本時,若前滾會刪除資料,應先將資料複製到暫存表(含主鍵),以便回滾時能還原資料並重建關聯約束。
此技術達成兩個目標:
- 部署自動化:不需擔心目標環境的資料庫當前狀態
- 解耦資料庫與應用程式變更:DBA 可獨立作業,將遷移腳本簽入版本控制,在開發者更新程式碼之前不會意外觸發
管理協調式變更(Managing Orchestrated Changes)#
在多個應用程式共用同一資料庫的情境下(雖然不建議,但現實中常見),變更可能產生連鎖效應。應對策略包含:
- 在系統整合測試(SIT)環境中測試變更,確認不影響其他應用程式
- 維護一份資料庫物件使用登記簿(Registry),記錄哪些應用程式使用哪些資料庫物件
- 讓應用程式能同時相容多個資料庫版本,使資料庫遷移可獨立於應用程式部署進行
**技術債(Technical Debt)**在資料庫設計中尤其重要。當資料庫作為多個系統的整合點時,設計上的技術債會產生廣泛的連鎖效應。應定期重構以控制技術債,並在刻意累積技術債前先制定償還計畫。
資料庫回滾與零停機發布(Rolling Back Databases and Zero-Downtime Releases)#
部署至正式環境時,有兩個常見的額外需求:
- 回滾時不丟失交易資料
- 零停機發布(Zero-Downtime Release / Hot Deployment)
不丟失資料的回滾(Rolling Back without Losing Data)#
以下情況可直接執行回滾腳本:
- Schema 變更不會遺失資料(如正規化 / 反正規化、欄位搬移)
- 被刪除的資料僅新版本系統使用,且可接受遺失
以下情況則需要特殊處理:
- 回滾涉及從暫存表還原資料,可能違反完整性約束
- 回滾涉及刪除不可遺失的新交易資料
解決方案一:快取與重播交易(Cache & Replay)
升級後記錄每筆交易(透過 UI 事件、系統訊息攔截或資料庫交易日誌),回滾後重新播放這些交易。
解決方案二:藍綠部署(Blue-Green Deployments)
- 發布時備份 Blue 資料庫,還原至 Green 資料庫並執行遷移
- 將使用者切換至 Green 環境
- 需要回滾時,直接切回 Blue 環境
- Green 環境中的新交易可事後復原
sequenceDiagram
participant 使用者
participant Blue環境
participant Green環境
participant 資料庫
Note over 使用者,Blue環境: 目前使用 Blue
Green環境->>資料庫: 備份 Blue 資料庫
Green環境->>Green環境: 還原並執行遷移
使用者->>Green環境: 切換路由到 Green
Note over 使用者,Green環境: 若出問題需回滾
使用者->>Blue環境: 切回 Blue 路由
Note over Blue環境: Blue 資料完整無損若資料量過大導致備份還原耗時過長,則無法使用獨立資料庫的藍綠部署方案;此時兩個環境需共用同一資料庫,在發布時切換連接。
將應用程式部署與資料庫遷移解耦(Decoupling Application Deployment from Database Migration)#
第三種方案是將資料庫遷移與應用程式部署分離為獨立步驟,如下圖所示:

Figure 12.1: Decoupling database migration from application deployment
具體做法:
- 不是每次發布都遷移資料庫;需要遷移時,先部署一個**過渡版本(Transitional Version)**的應用程式,使其同時相容當前與新版資料庫
- 確認新版應用程式穩定後,再執行資料庫遷移
- 下一個應用程式版本只需相容新版資料庫即可
這種做法的優勢:
- 資料庫遷移前可充分觀察新版應用程式的行為
- 即使資料庫升級涉及不可逆的 Schema 變更,也能在升級前確認不需回滾
前向相容(Forward Compatibility)——較舊版本的應用程式能在較新版本的 Schema 上運作——是日常變更的預設策略。最佳實踐是盡量讓變更具有加法性(Additive):新增資料表或欄位,而非修改現有結構。
另一種管理資料庫變更的方式是使用抽象層(Abstraction Layer),例如預存程序(Stored Procedures)與檢視(Views)。透過抽象層存取資料庫,可以在不影響應用程式的前提下修改底層資料庫物件——這是**抽象分支(Branch by Abstraction)**模式的一種應用。
管理測試資料(Managing Test Data)#
測試資料管理有兩個核心關注點:
- 測試效能(Test Performance):確保測試盡可能快速執行
- 測試隔離(Test Isolation):確保每個測試在定義明確的環境中執行,不受其他測試影響
單元測試中偽造資料庫(Faking the Database for Unit Tests)#
單元測試不應依賴真實資料庫。推薦做法:
- Repository 模式:建立資料存取的抽象層,解耦應用程式與資料庫,測試時替換為測試替身(Test Double)
- 記憶體內資料庫(In-Memory Database):使用 H2、SQLite 或 JavaDB 等工具,讓單元測試跑在記憶體資料庫上

Figure 12.2: Abstracting database access
Repository 模式不僅支援測試,還能促進業務邏輯與資料儲存的分離,並集中管理所有資料存取程式碼。
測試與資料之間的耦合管理(Managing the Coupling between Tests and Data)#
管理測試狀態有三種策略:
| 策略 | 說明 | 建議 |
|---|---|---|
| 測試隔離(Test Isolation) | 每個測試的資料僅對該測試可見 | 強烈推薦 |
| 適應性測試(Adaptive Tests) | 測試評估資料環境並調整行為 | 不易擴展 |
| 測試排序(Test Sequencing) | 測試按已知順序執行,依賴前一測試的輸出 | 不易擴展 |
測試隔離是首選,它讓測試更靈活,且能平行執行以提升效能。
測試隔離的實作方式#
- 交易回滾(Transaction Rollback):在測試開始時建立交易,結束時回滾,利用資料庫的交易隔離特性確保資料不會被其他測試看見
- 資料分區(Functional Partitioning):讓每個測試建立的實體遵循特定命名慣例,確保測試只看到自己建立的資料
Setup 與 Tear Down#
無論選擇哪種策略,在測試前建立已知的初始狀態、測試後恢復狀態,對於避免測試間的交叉依賴至關重要。
關於連貫測試場景(Coherent Test Scenarios)#
應抵抗建立「連貫故事線」的誘惑——即讓測試按順序串聯、共用資料以減少 Setup/Tear Down。這會導致測試之間的緊耦合,帶來連鎖失敗、難以維護等問題。實務上,在每個步驟我們都需要探索成功、失敗、邊界條件等多種情境,測試隔離是更務實的選擇。
資料管理與部署管線(Data Management and the Deployment Pipeline)#
提交階段的測試資料(Data in Commit Stage Tests)#
提交測試的核心原則:
| 原則 | 說明 |
|---|---|
| 速度至上 | 每多 30 秒都是成本 |
| 最少資料 | 每個測試只使用直接影響待測行為的最少量資料 |
| 避免過度耦合 | 如果建立測試資料很費力,代表設計需要更好的分解 |
| 共用 Helper/Fixture | 將常用資料結構的建立邏輯封裝在如 CustomerHelper 或 CustomerFixture 等共用類別中,提供一致的預設值,各測試再按需調整 |
若測試與實作細節耦合過深,小幅實作變更就會導致大量測試修改,測試將從行為的守護者變成變更的阻礙者。
驗收測試的測試資料(Data in Acceptance Tests)#
驗收測試是系統級測試,資料管理更為複雜。設定應用程式狀態時,應區分三類資料:
| 資料類別 | 說明 |
|---|---|
| 測試專屬資料(Test-Specific Data) | 驅動待測行為的核心資料,應確保唯一性與隔離性 |
| 測試參考資料(Test Reference Data) | 測試所需但不直接影響行為的配角資料,可預先載入並跨測試共用 |
| 應用程式參考資料(Application Reference Data) | 與測試行為無關但應用程式啟動所需的資料,甚至可為 null 值 |
驗收測試中,建議透過應用程式的公開 API 而非直接操作資料庫或應用程式內部程式碼來建立測試狀態。好處包含:避免不一致狀態、重構不影響測試、同時測試了 API 本身。
應用程式參考資料與測試參考資料可以資料庫 Dump 的形式保存,並隨應用程式版本一同遷移,這也是測試自動化遷移策略的好時機。
容量測試的測試資料(Data in Capacity Tests)#
容量測試面臨資料規模的挑戰。推薦策略是重用驗收測試的資料與互動模式,透過工具將其放大為多個並行案例。
這種做法基於一個前提:驗收測試已捕捉了系統的重要互動行為,作為行為的可執行規格(Executable Specifications),它們同樣適合作為容量測試的基礎。
其他測試階段的資料#
- 自動化測試階段:同樣以驗收測試為起點,重用「行為規格」
- 手動測試階段(探索式測試、使用者驗收測試):
- 載入最小資料集讓應用程式從空白狀態啟動,或
- 載入較大資料集模擬使用一段時間後的狀態
- 不建議使用正式環境的完整資料 Dump——通常太大且不實用
- 建議建立客製化資料集,來源可以是正式資料的子集或自動化測試執行後的資料庫
開發者絕對不應在開發環境使用正式環境的資料集。
總結#
本章的核心原則與實踐:
- 版本控制你的資料庫,使用 DbDeploy 等工具自動管理遷移
- 同時維持前向與後向相容性,將資料庫部署問題與應用程式部署問題解耦
- 測試應自行建立所需資料,並透過資料分區確保不影響同時執行的其他測試
- 僅共用應用程式啟動所需的基礎資料與極為通用的參考資料
- 盡量透過應用程式的公開 API 建立測試狀態
- 不要使用正式環境的資料 Dump 進行測試;應建立客製化的較小資料集
自動化的資料庫遷移流程應貫穿整個交付管線——從開發環境的最小資料集到正式環境的完整遷移,使用同一套流程,確保可重複且可靠。