Schema 演化與相容性 (Compatibility)

★★★★★Reviewed

FromDDIA

📚 From the Books

Schema 演化與相容性 (Compatibility)Encoding & Evolution

Schema 會隨需求改變,但你不可能讓所有節點同時升級。相容性就是「新舊版本的程式碼與資料能否互通」的契約。核心洞見:資料比程式碼活得久,所以 schema 必須能向前向後演化。

🧠 Intuition

TIP

別記方向,記角色:相容性永遠是「誰來讀誰寫的資料」。向後相容 = 新程式讀舊資料(新人讀得懂前輩留的東西);向前相容 = 舊程式讀新資料(前輩讀得懂新人寫的、自動略過看不懂的部分)。

⚖️ Tradeoffs

向後相容 — 新讀舊
  • 場景:你部署了 v2 程式,但 DB 裡還有 v1 寫的舊紀錄。
  • 規則:新增欄位若 reader 期望但舊資料沒有,必須有預設值可補;不能新增「沒預設值的必填欄位」。
  • 通常較容易達成,因為新程式作者知道舊 schema 長什麼樣。
向前相容 — 舊讀新
  • 場景:v2 程式已開始寫新欄位,但還沒升級的 v1 程式得讀這些紀錄。
  • 規則:舊程式必須安全略過不認識的欄位(Protobuf 靠未知 tag 略過、Avro 靠 schema resolution 略過 reader 沒有的欄位)。
  • 較難,因為「未來的欄位」舊程式無從預期——格式本身得設計成可略過未知資料。
欄位增刪規則
  • 新增:只能加 optional / 有預設值的欄位(同時保住向前與向後相容)。
  • 刪除:只能刪 optional 欄位;刪必填欄位會讓舊資料 / 舊程式爆炸。
  • 識別子
    的 field tag、Avro 的欄位名一旦用過就不可改用途——改了等於把舊資料對應到錯欄位。
  • 改型別
    ,僅限可安全轉換者(如 int32 → int64),且可能損精度。
Avro 的 schema resolution
  • Avro 解碼時同時看 writer schema(寫入時)與 reader schema(讀取時),逐欄位對齊。
  • reader 有、writer 沒有的欄位 → 用 reader schema 的預設值補(撐起向後相容)。
  • writer 有、reader 沒有的欄位 → 直接略過(撐起向前相容)。
  • 因此 Avro 把「兩種相容性」都收斂成同一套 resolution 規則,但前提是 reader 必須能拿到 writer schema。

🔑 Takeaways

✍️ My Notes

2026-07-01

  • Q: rolling upgrade 為什麼向前、向後相容兩種都要
  • A: 因為滾動升級期間,新舊版本程式同時在線。已升級的新節點會讀到舊節點寫的紀錄(需要向後相容:新讀舊),而還沒升級的舊節點也會讀到新節點寫的紀錄(需要向前相容:舊讀新)。任何一個方向破了,升級窗口內就會有節點讀資料報錯。這跟「一次性全停機升級」不同——後者只需向後相容。也因此資料庫場景更嚴苛:DB 裡同時躺著好幾代 schema 寫的資料,程式必須對所有歷史版本都向後相容。

📖 Further Reading

🔗 Dive Deeper