Chapter 12: Managing Data#

引言#

資料(Data)及其管理為測試與部署流程帶來兩大獨特挑戰:

  • 龐大的資料量:應用程式的狀態資料通常遠超原始碼與設定檔的體積
  • 生命週期差異:應用程式資料必須被保留與遷移,資料往往比建立它的應用程式更長壽;部署新版本或回滾時,資料不能像程式碼一樣直接抹除重來

一旦系統上線,資料就會持續增長,並擁有獨立的價值——資料可以說是整個系統中最有價值的部分。因此,我們必須建立機制,讓資料結構或內容的變更能在最小化風險的前提下完成。其中最關鍵的做法就是自動化資料庫遷移流程(Automating Database Migration)

本章主要以**關聯式資料庫(RDBMS)**為討論對象,但所提出的原則同樣適用於其他資料儲存系統(包含 NoSQL)。


資料庫腳本化(Database Scripting)#

所有資料庫的變更——初始化、遷移——都應被腳本化(Scripted)並存入版本控制(Version Control)。這些腳本應能服務於交付流程中的每一個環境:

  • 開發者在本地建立新資料庫
  • 系統整合測試(SIT)環境的升級
  • 正式環境的資料庫遷移

資料庫的 Schema 會隨應用程式演進,因此必須確保部署到任一環境時,資料庫 Schema 能與對應版本的應用程式相匹配。

此外,資料庫腳本也應納入持續整合(CI)流程。驗收測試的前置作業應包含:以正確 Schema 建立資料庫,並載入必要的測試資料。

資料庫初始化(Initializing Databases)#

資料庫初始化是最簡單也最基本的起點。初始化腳本應依序執行:

  1. 建立資料庫結構——資料庫實例、Schema、使用者帳號等
  2. 載入參考資料——應用程式啟動所需的基礎資料

對於少數資料為暫時性或唯讀的簡單專案,每次部署時直接「抹除重建」即是最有效的策略。

如果你的場景允許每次部署都從零開始建立資料庫,那就直接這麼做——這是最簡單的策略。


漸進式變更(Incremental Change)#

對於大多數系統,部署時必須在保留現有資料的前提下更新資料庫。這包含三個面向:

  • 持續整合要求每次變更後應用程式仍能正常運作(含資料庫變更)
  • 持續交付要求任何成功的候選版本都能部署至正式環境
  • 需要有**回滾策略(Rollback Strategy)**以應對部署失敗

資料庫版本控制(Versioning Your Database)#

最有效的自動化遷移機制是為資料庫加上版本號:

  1. 在資料庫中建立一張表記錄當前版本號
  2. 每次變更建立兩個腳本:
    • 前滾腳本(Roll-Forward Script):從 version x 升級到 version x+1
    • 回滾腳本(Roll-Back Script):從 version x+1 降回 version x
  3. 應用程式的設定中記錄其所需的資料庫版本

部署時,工具(如 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)#

部署至正式環境時,有兩個常見的額外需求:

  1. 回滾時不丟失交易資料
  2. 零停機發布(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

具體做法:

  1. 不是每次發布都遷移資料庫;需要遷移時,先部署一個**過渡版本(Transitional Version)**的應用程式,使其同時相容當前與新版資料庫
  2. 確認新版應用程式穩定後,再執行資料庫遷移
  3. 下一個應用程式版本只需相容新版資料庫即可

這種做法的優勢:

  • 資料庫遷移前可充分觀察新版應用程式的行為
  • 即使資料庫升級涉及不可逆的 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將常用資料結構的建立邏輯封裝在如 CustomerHelperCustomerFixture 等共用類別中,提供一致的預設值,各測試再按需調整

若測試與實作細節耦合過深,小幅實作變更就會導致大量測試修改,測試將從行為的守護者變成變更的阻礙者。

驗收測試的測試資料(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 進行測試;應建立客製化的較小資料集

自動化的資料庫遷移流程應貫穿整個交付管線——從開發環境的最小資料集到正式環境的完整遷移,使用同一套流程,確保可重複且可靠。