大家好,我是Java烘焙師。本文結(jié)合筆者的經(jīng)驗和思考,對業(yè)務(wù)數(shù)據(jù)遷移做個總結(jié)。在業(yè)務(wù)系統(tǒng)迭代的過程中,難免會做數(shù)據(jù)遷移,可能是因為:
- 業(yè)務(wù)模型變更:實體關(guān)系變了
- 或者存儲結(jié)構(gòu)變更:字段從DB遷移到HBase等NoSQL存儲、或做文本壓縮、或修改數(shù)據(jù)聚合粒度等
業(yè)務(wù)數(shù)據(jù)遷移,需要改持久化層基礎(chǔ)代碼,還涉及到存量數(shù)據(jù)遷移,因此風(fēng)險較高,容易出現(xiàn)新舊數(shù)據(jù)不一致。為了實現(xiàn)平滑遷移(不影響、不中斷業(yè)務(wù)),各步驟都要做好灰度切換、數(shù)據(jù)/監(jiān)控對比。
下面總結(jié)了一套標準流程SOP,步驟如下。
1. 增量雙寫
- DB遷移場景:寫舊表+寫新表,放在同一個事務(wù)內(nèi),即可保證一致性
-
跨DB、或從DB遷移到其它NoSQL存儲場景:先寫舊表,再寫新表,在寫失敗時做好兜底重試
image.png
2. 刷存量數(shù)據(jù)
在增量雙寫灰度開全后,能確保新寫入的數(shù)據(jù)在新表、舊表一致,但還需要補刷歷史存量數(shù)據(jù)。
離線導(dǎo)出全量數(shù)據(jù)的業(yè)務(wù)id,查舊表、并寫新表,確保所有存量數(shù)據(jù)的新表字段都寫入值。

image.png
3. 數(shù)據(jù)全量比對,確保新舊一致
有兩種比對方式:
- 復(fù)用上述刷數(shù)據(jù)任務(wù),通過開關(guān)控制,僅做比對、不刷數(shù)據(jù)
- 離線對賬
參考之前寫的文章:架構(gòu)師必備:實時對賬與離線對賬
sql示例:
-- (1)比較兩個表的條數(shù)是否一致
select count(1) from table_a; -- 查出左表的條數(shù)
select count(1) from table_b; -- 查出右表的條數(shù)
-- (2)比較數(shù)據(jù)內(nèi)容是否一致
-- (2.1)只存在于左表的數(shù)據(jù):左連接查詢,左表記錄都會保留,右表字段為空則說明右表缺少數(shù)據(jù):
select * from
table_a left outer join table_b
on table_a.biz_field=table_b.biz_field
where table_b.biz_field is null;
-- (2.2)只存在于右表的數(shù)據(jù):右連接查詢,右表記錄都會保留,左表字段為空則說明左表缺少數(shù)據(jù):
select * from
table_a right outer join table_b
on table_a.biz_field=table_b.biz_field
where table_a.biz_field is null;
-- (2.3)兩個表存在差異的數(shù)據(jù),內(nèi)連接查詢,比對各個字段是否一致:
select * from
table_a inner join table_b
on table_a.biz_field=table_b.biz_field
where
(table_a.field_1 <> table_b.field_1
or table_a.field_2 <> table_b.field_2
or table_a.field_3 <> table_b.field_3);
4. 增量雙讀比對
上述全量比對,只代表存量數(shù)據(jù)的一致性,還要確保增量數(shù)據(jù)也是一致的。
在所有查詢場景中,同時讀舊表、新表,并做比對。為了避免遺漏,可做到公共查詢邏輯里。

image.png
5. 切新讀
- 雙讀比對觀察一致后,查詢場景灰度切換到讀新表
-
同時確保所有的離線依賴,都遷到新表數(shù)據(jù)源
image.png
6. 停舊寫
- 在停寫舊表之前,檢查舊表已無在線業(yè)務(wù)方查詢、無離線依賴
-
最終停寫舊表
image.png
通過以上步驟,可以確保業(yè)務(wù)數(shù)據(jù)平滑遷移,做到風(fēng)險可控。


