Schema 主表翻轉的 dual-write 過渡:一場不能 stop-the-world 的搬家
引言:當業務主表需要翻轉 某個 SaaS 系統長期以「客戶資料表」為核心:所有訂單、文件、操作紀錄都用客戶 ID 當外鍵。但這個客戶表的資料來源是外部 POS 系統匯入,每天同步幾百筆,schema 由廠商定義。 業務發展後問題浮現:自己 SaaS 的會員表(users)才是真正的「人」 — 有登入、有偏好、有應用內行為。新功能(個人化推薦、訂閱管理、社交綁定)都需要以 users 為主軸。 於是有了一個經典的 schema migration 需求:主表翻轉(primary table pivot)。把業務邏輯的中心從 customers(外部 POS 匯入)轉到 users(SaaS 自家會員),但歷史資料、新進資料、系統相依、回滾風險全部都要顧到。 名詞解釋 開始拆解之前,定義幾個會反覆出現的詞: 詞 定義 主表翻轉(Primary table pivot) 業務主要 entity 從表 A 改成表 B Dual-write 寫入時同時寫舊欄位 + 新欄位,回滾安全 Partial cut-over 分階段切換,read 跟 write 不同步切 Hard cut-over(stop-the-world) 一次切完,downtime 短但 risk 大 Backfill 歷史資料補齊新欄位的 batch update Idempotent migration 重跑無副作用,cron / retry 安全 DISTINCT ON PostgreSQL 專屬去重 syntax,搭 ORDER BY 取每組第一筆 Pseudo entity 為了統一查詢介面而建的「假」實體 Link table ORM 多對多 join 表(user_id + entity_id) 為什麼選 dual-write 而非 stop-the-world 最直接的搬家方式是「stop-the-world cut-over」:選一個維護窗口,停寫入、跑 batch script、改完所有 reference、開機。但這個 SaaS 的條件不允許: ...