真實線上DB存儲架構升級實戰!
前言
交易系統1.0的存儲架構采用了單庫單表,隨著業務快速發展,訂單量從日單量十萬級別快速增長到日單量百萬級別,預計在未來兩三個月就存儲就會出現瓶頸,DB存儲架構升級迫在眉睫。
交易系統如何做到平滑遷移?
由于面對業務場景是交易系統,即使是業務低峰期,業務也無法接受臨時停服,基于此背景下,設計了如下一套不停服下的數據平滑遷移工具。
平滑遷移流程設計
由于面對的業務場景是交易系統,即使是業務低峰期,依舊有交易在不斷產生,所以對于線上DB停寫從業務角度是不能接受的,同時如何實現新舊數據源的靈活切換、切換前的充分驗證及觀測,以及切換后如果出現問題如何快速回滾,基于這些問題設計了一套雙寫雙讀流程。
切換前充分驗證和可觀測性: 在整個切換過程中的關鍵環節都增加監控數據埋點上報,從而實現整個操作過程的可觀測,也保證了在每一個環境切換前的充分驗證。
動態配置下發: 將切換過程中需要調整的參數全部寫入動態配置中心(比如Nacos),并為每個動態下發參數設計灰度放量的過程,比如基于用戶尾號放量、基于租戶維度放量等,保證在切換過程中的影響范圍做到可控。
具體操作流程如下:
a). 對業務代碼進行改造上線,使得具備雙寫讀舊數據源的能力,通過Grafana監控讀寫曲線及異常監控等指標。
b). 通過離線任務或者DBA提供的數據同步組件,將舊數據源的數據開始全量同步。
c). 待全量數據同步完成后,通過動態配置中心開始逐步放量,將雙寫讀舊數據源切換到雙寫讀新數據源,同時通過Grafana監控讀寫曲線及異常監控等指標.
d). 灰度一段時間后,若未發現異常時,在通過動態配置中心逐步灰度切換到單寫讀新數據源,最后下線舊數據源。在第3、4步驟切換過程中一旦發現問題立刻對動態配置進行回滾。
圖片
切流組件實現
平滑切流組件的執行流程圖如下圖所示:
圖片
對應的核心執行代碼如下:
public T call() {
try {
// 1. 灰度開關關閉,走老邏輯
if (Objects.isNull(shardConfig)) {
return oldCall.call();
}
Callable<T> mainCall = oldCall;
Callable<T> subCall = newCall;
// 2. 業務是否使用新數據源數據返回的結果
if (shardConfig.isUseNewResult()) {
mainCall = newCall;
subCall = oldCall;
}
// 未命中灰度,則直接返回主邏輯結果
if (!isGray()) {
return result;
}
// 是否開啟異步數據對比旁路邏輯
if (shardConfig.isAsync()) {
Callable<T> finalSubCall = subCall;
Thread thread = new Thread(() -> {
process(finalSubCall, result);
});
EXECUTOR_SERVICE.submit(thread);
} else {
// 同步鏈路數據對比
process(subCall, result);
}
return result;
} catch (Exception e) {
throw new RuntimeException("call error");
}
}
/**
* 對新邏輯返回的結果和舊邏輯返回的數據進行對比
* @param subCall 子邏輯結果
* @param result 主邏輯結果
*/
private void process(Callable<T> subCall, T result){
// TODO 對新邏輯返回的結果和舊邏輯返回的數據進行對比
try {
T subReuslt = subCall.call();
boolean isSame = shardComparator.compare(result, subReuslt);
if (!isSame) {
// TODO 上報對比異常監控
PerfHelper.report("methodName", methodName);
}
} catch (Exception e) {
LOGGER.error("compare exception");
}
}
private boolean isGray() {
// TODO 業務灰度規則
return true;
}
交易系統平滑遷移過程中的數據一致性如何保證?
圖片
用戶下單后需要冗余三個視角維度的數據,對于C端用戶視角的數據一致性,采用雙寫+binlog補償+定時任務兜底;對于B端商家視角的數據一致性,使用雙寫+binlog補償+定時任務兜底+數據回源四種手段保證數據一致性。最后通過數據流對賬+可視化監控來實時觀測數據不一致的情況,并對異常數據進行手動修復。
遇到的一些坑
下單鏈路代碼不合理
從單庫單表切換到分庫分表后,通過監控發現讀新數據源數據對比錯誤量放大,原因是原來的下單鏈路存在:更新完DB后立馬查詢,由于主從數據同步還未完成導致無法查詢到最新數據。
解決思路: 調整業務邏輯
數據對比采用線程池進行異步對比導致監控異常
起初使用線程池進行異步對比,是為了盡量不影響主鏈路耗時,但由于線程池使用姿勢不太合理,導致實際發生比對時新舊數據源已經不一致,其根本原因是由于異步延遲執行導致。
解決思路: 1. 數據對比改為同步執行,主鏈路耗時無明顯劣化,業務可接受;2. 動態調整線程池參數,使得異步任務執行更快處理(該方法嘗試后,發現錯誤量有所下降,但未完全消除。
后續
本文介紹了一個線上交易系統從單庫單表架構升級為多庫多表過程中,如何實現不停服平滑遷移的方案,同時在整個遷移過程中實現了可觀測、可回滾。
隨著訂單量持續增長,后續B端商家側的數據逐漸出現了數據傾斜、大賬單等問題,后續有機會再介紹這些問題的解決思路和治理方案。