題目與範圍
服務有大規模 orders 表、子表、公開 URL、事件載荷與副本。PostgreSQL 18 提供原生 uuidv7() 產生器。請在保持 UUIDv4 合約的同時讓新紀錄逐步使用 UUIDv7,避免表重寫和長時間阻塞鎖。
核心能力是跨 API、儲存與非同步消費者的線上身分與 Schema 遷移,因此歸入 backend。
面試官考察什麼
第一,能否區分識別格式與時間真相?UUIDv7 含有時間排序資訊,但不是嚴格全域提交序列,不能取代業務時間戳。
第二,能否維持公開身分?舊 URL、快取、冪等鍵、事件與外鍵在遷移期間都要繼續解析。
第三,能否設計雙讀雙寫階段並確定唯一事實來源?
第四,能否在多副本、多地區環境分批回填,同時控制寫入放大和複製延遲?
第五,能否用實測驗證區域性和延遲,而不是假設 UUIDv7 對所有負載都更快?
先釐清的問題
- UUID 是否對外暴露、作為主鍵,還是兩者都是?
- 客戶端能否接受新識別,還是舊 UUID 必須維持規範身分?
- 有多少子表、索引、事件和搜尋文件引用該鍵?
- 所有寫入器和副本使用哪些 PostgreSQL 版本與擴充?
- 寫入速率、複製延遲 SLO 和回滾期限是多少?
- 排序需求用於分頁、稽核展示,還是只有索引區域性?
30 秒回答框架
「我會保留 UUIDv4 作為穩定公開識別,新增 UUIDv7 映射欄位,先上線雙寫再回填。讀取同時接受兩種鍵,子表引用與事件保持相容。回填使用有界主鍵範圍,監控延遲與鎖;完成一致性檢查後再切換內部索引與查詢路徑。UUIDv7 可以改善區域性和時間範圍掃描,但業務時間仍需顯式保存。全部消費者驗證前,用特性開關支援回滾。」
分步作答
第一步:選擇相容形狀
不要靜默改變現有 UUID 的含義。保留 orderidv4 作為外部合約,新增 orderidv7 與唯一約束,或使用獨立內部鍵與持久映射。明確每個階段子表和事件使用哪個鍵。
ALTER TABLE orders ADD COLUMN order_id_v7 uuid;
CREATE UNIQUE INDEX CONCURRENTLY orders_order_id_v7_uq
ON orders(order_id_v7) WHERE order_id_v7 IS NOT NULL;只有目標 PostgreSQL 提供 uuidv7() 時才直接使用;否則先部署一致的產生器。
第二步:先部署寫入
新寫入在同一交易產生兩種識別並記錄映射。暫時無法填入新欄位的舊寫入仍有效。讓重試冪等,避免重複命令產生兩個映射。
第三步:增加雙讀解析
API 接受任一識別,解析到同一個規範訂單,舊回應繼續回傳 v4。新端點可在版本化合約中暴露 v7。快取鍵使用解析後的規範身分,避免兩種形式分叉。
第四步:分批回填
使用穩定游標回填,小交易執行;複製延遲、鎖等待或寫入延遲超閾值就暫停。資料足夠後再並行建立新索引。父映射持久化前不要更新子表。
第五步:遷移引用和事件
重疊期間保持子表外鍵和事件 Schema 相容。v7 欄位先設可選,同時發布兩個值;所有消費者升級後再設為必填。切換約束前先對帳缺失與重複映射。
第六步:切換內部存取路徑
一致性檢查通過後,在區域性或時間掃描受益的內部連接和分頁中使用 UUIDv7。業務排序仍使用顯式 created_at 和穩定 tie-breaker。
第七步:驗證與觀測
在匹配負載下比較索引大小、頁分裂、快取、插入延遲、範圍掃描延遲、複製延遲和錯誤率。確認 v4 與 v7 查詢返回同一行,事件消費者仍冪等。
第八步:回滾窗口後再退役
所有客戶端、回放工具、匯出、複製和副本通過遷移窗口前,保留映射、舊索引和雙讀。舊約束分獨立部署移除,並準備回滾。
示例回答
「我不會原地改寫公開 UUID 合約,而是新增 UUIDv7 欄位和唯一映射,先雙寫,再讓雙讀解析兩種形式。回填使用有界交易,監控鎖、複製延遲與寫入延遲。子表與事件在過渡期同時攜帶兩種 ID,消費者升級後再切換。完成一致性與區域性實測後,切換內部連接和分頁,但業務排序保留 created_at。特性開關在舊路徑退役前支援反向切換。」
常見錯誤
- 立即替換公開 UUID → 連結和事件中斷 → 保留規範合約與映射。
- 把 UUIDv7 當嚴格時間序 → 分頁跳過或重排 → 使用顯式時間戳與 tie-breaker。
- 一筆大交易回填 → 鎖與複製延遲尖峰 → 分批執行。
- 映射未完成就加外鍵 → 過渡寫入失敗 → 先父表、再子表、最後約束。
- 混合不支援版本產生 UUID → 語意不一致 → 固定版本或統一產生器。
- 忽略重試 → 重複映射 → 讓雙寫冪等。
- 過早刪除 v4 → 舊匯出和回放失敗 → 等待回滾窗口結束。
追問
追問 1:UUIDv7 能取代 created_at 嗎?
不能。它可改善區域性並攜帶時間位,但業務排序需要顯式時間戳和確定性 tie-breaker。
追問 2:v4 和 v7 能共用 UUID 欄位嗎?
UUID 型別可以容納兩種格式,但遷移元資料、公開合約和排序語意仍需明確規劃。
追問 3:為什麼先雙讀再切換寫入?
如此在回填和消費者升級未完成時,新舊紀錄都能一致解析。
追問 4:如何限速回填?
採用有界交易,複製延遲、鎖等待、CPU 或寫入延遲超閾值就暫停,從持久游標恢復。
追問 5:事件必須包含什麼?
過渡期間發布兩個 ID 或穩定映射引用,版本化 Schema,消費者升級後再強制 v7。
追問 6:什麼時候刪除舊欄位?
客戶端、匯出、回放、副本和回滾檢查通過約定窗口後,再分獨立部署刪除約束與索引。