背景
隨著財經支付業務的快速發展,考慮到未來訂單量持續增長,在線存儲遇到更大的挑戰,需提前做好規劃。目前財經支付主要業務都是使用 mysql(InnoDB)作為數據存儲,因歷史訂單信息訪問頻率低并占用了大量數據庫存儲空間,期望將歷史數據跟生產最新交易數據進行分離,當前數據庫保留最近一段時間的數據作為熱庫,歷史交易存入另一個數據庫壓縮存儲作為冷庫(rocksdb),即數據庫冷熱分離。此舉將會極大的節省數據庫設備成本,減少因在線存儲空間不足擴容導致停服不可用的時長,以下基于財經支付的統一交易系統現狀做的相關案例分析僅供大家參考。
方案
技術選型
架構圖
方案分析
因業務場景比較復雜,如果按照業務場景梳理工作量將幾何倍的增長,換種維度,數據庫相關的操作無非就是查詢、插入、更新,只要能在數據庫交互層實現保證查詢、插入、更新這些數據庫基本操作在增加冷熱分離后功能不受影響即可。財經支付代碼有統一的分層規范,對數據庫操作全部收斂封裝至數據庫交互層,因此比較好改造,不擴容的情況下,熱庫預計保留最近 X 天(時間可調)數據, X 天前的數據歸檔到冷庫。
方案對比
方案一:能夠徹底數據庫存儲壓力的解決方案,但是對冷庫性能要求太高,如果涉及的插入、更新、查詢能夠根據單號過濾時間,降低對冷庫的依賴。
方案二:適用于冷庫性能較低,涉及的插入、更新、查詢大部分無法根據單號過濾時間時,需要熱庫歸檔表中轉過濾。
方案三:如果系統涉及的場景比較簡單,歷史訂單后續也無變更,可以按場景進行歸檔。
方案選擇
交易:交易表負責記錄商戶訂單與財經支付內部訂單的映射、交易金額、買方和賣方等重要信息,最重要的功能是防止重復交易。但是冷庫相比熱庫性能較低,商戶訂單號無固定規則,無法根據單號判斷時間過濾減少冷庫壓力,而熱庫 cpu 使用率很低,熱庫數據庫計算不是瓶頸,因此交易選用方案二。交易歸檔表主要意義在于減少在線交易對冷庫的依賴。
支付:支付表負責保存交易單用了什么支付方式、該支付方式需要扣多少錢、從哪里扣、扣到哪里去等信息,涉及的訂單查詢、更新、插入都可以根據交易單號或者支付單號進行判斷時間減少對冷庫的查詢,因此支付選擇方案一。
基本原則
為了充分的保證 0 事故,0 資損,方案設計時,提出了以下基本原則,在研發、測試、代碼評審時均參考以下基本原則進行層層把控,可以有效的避免生產事故的發生。
數據插入唯一性:
- 熱庫歸檔表的所有唯一鍵必須跟要歸檔的熱庫表保持一致。
- 熱庫歸檔記錄已存在的訂單,冷庫必須有相應數據,
- 冷庫插入: 先 insert 冷庫成功后 再 insert 熱庫歸檔表
- 冷庫更新:更新冷庫數據,使用同一個事務 先 delete 再 insert 冷庫數據
- 熱庫刪除:刪除熱庫數據時使用冷庫數據當 where 條件,所有熱庫字段(包含 ID)條件都滿足后才能刪除成功。
數據更新一致性:
- 冷庫無 update 操作,所有的更新操作必須在熱庫進行,如果數據需要更新并且僅在冷庫存在,需同步到熱庫后,再在熱庫完成更新。
- 冷庫熱庫數據同時存在時,以熱庫數據為準。冷庫的數據來源只有熱庫數據同步到冷庫。
- 數據從冷庫同步到熱庫時,操作歸檔表和交易表需保證在同一個是事務內完成,涉及到的查詢必須使用寫庫。
數據查詢準確性:
- 單筆查詢:查詢熱庫數據不存在時,不存在再次查詢冷庫(如果單號中可以判斷訂單日期,可再增加一層日期過濾,減少冷庫查詢)
- 批量查詢:冷庫熱庫數據都存在時優先返回熱庫數據。
- 批量查詢:合并冷熱庫數據后,需看原先查詢的接口順序是否有要求,如果對順序有要求合并后還需排序。
- 減少冷庫壓力:冷庫性能較低,線上實時交易盡可能減少對冷庫的查詢和依賴(可通過交易單號中的日期或者歸檔表進行過濾)。
- 限制天數控制:數據庫交互 層天數控制 為 n,歸檔任務控制的天數為 m,要求 m>n。例如,mode 層 有些判斷訂單超過 n 天的才會查詢冷庫,歸檔任務只歸檔 m 天前的歷史數據,分開控制可以防止因調整歸檔天數導致數據查不到情況。
具體細節
歸檔表結構
歸檔表狀態流轉
一致性刪除
使用冷庫記錄的所有字段當作刪除熱庫的 where 條件(包含自增 id),刪除熱庫和更新熱庫歸檔狀態為冷庫需在一個事物,任一失敗則回滾。
交易及支付任務(數據歸檔、刪除、兜底)
歸檔任務
查詢熱庫訂單表 X (時間可調)天前的訂單,同步熱庫訂單到冷庫,插入熱庫歸檔表,歸檔狀態為處理中,放入延遲刪除 mq 消息。
歸檔刪除 TASK
常駐服務 TASK 消費刪除 mq 消息,rpc 調用交易支付提供的刪除接口,支持本地限流能力。
兜底任務:
主要功能:查詢熱庫歸檔表中處理中修改時間超過規定時間的訂單強制執行刪除操作。主要用來防止 mq 異常或者日常丟失消息時,使用兜底任務可以補償消化處理中的歸檔記錄。
執行邏輯
數據歸檔任務(每天啟動一次)
for {
初始化查詢時間范圍和分頁
for{
查詢 X 天前交易單 limit 1000(索引排序,滾動時間查詢)
if 記錄存在 并且 條數=1000 {
for 對于每條記錄 {
// 啟用x個協程處理
交易單冪等寫入冷庫(不保證最新,只保證冷庫數據存在性)
冪等寫歸檔記錄表(type: PROCESSING,熱庫數據刪除時再更新為COLD,歸檔記錄已存在HOT狀態更新為PROCESSING )
發MQ延遲消息,X min(可配置)后刪除熱庫數據
}
}
if 條數=1000 {
continue
}
時間范圍往下推進
//記錄不存在
if 結束時間超過規定時間{
break (跳出循環,任務結束)
}
redis記錄當前查詢條件,方便后續任務重跑恢復繼續
}
}
刪除熱庫數據,消費MQ
消費MQ記錄 {
查詢冷庫
數據一致性刪除(開啟事務 條件刪除熱庫數據,更新歸檔記錄表狀態為COLD 結束事務)
一致性刪除熱庫失敗,同步熱庫數據到冷庫,數據一致性刪除
}
兜底補償任務(每30分鐘啟動一次)
{
查詢歸檔記錄表中狀態為PROCESSING,修改時間為X +Y min前的記錄 limit 1000
if 不存在 {
break
}
for 對于每條記錄
查詢冷庫
數據一致性刪除(開啟事務 條件刪除熱庫數據,更新歸檔記錄表狀態為COLD 結束事務)
一致性刪除熱庫失敗,同步熱庫數據到冷庫,數據一致性刪除
}
}
歸檔任務查詢時間滾動機制:時間范圍第一次起始時間為固定日期(財經支付訂單最早點日期),結束時間為指定日期,下次開始時間等于上次結束時間,結束時間為上次結束時間加上指定時間范圍)。每次查詢下一個時間窗口時 redis 保存信息,指定日期,當天任務的時間范圍,分頁數。
歸檔任務并發處理:需支持多任務分片并發處理
提升全天歸檔訂單量:為了不影響在線交易,全天 24 小時 區分 交易高峰、低峰、日常 三個不同時間段,歸檔速度不同。
交易-有歸檔表(查詢、新增、更新)
特點: 唯一鍵有外部單號,訂單規則隨機無法根據單號判斷時間,因此必須有歸檔表。
查詢
邏輯在數據庫交互層統一實現處理
存在以下部分情況可做特殊處理減少數據庫冷庫依賴。
- 單筆查詢:
- 根據外部單號查詢,如果查詢的 qps 較高,可以查詢冷庫前使用歸檔表進行過濾判斷。
- 根據交易單號查詢,如果可以根據單號判斷時間,查詢冷庫前使用單號進行時間范圍過濾。
- 批量查詢:部分功能管理后臺功能分頁查詢,對數據查詢范圍要求較高的增加冷庫查詢邏輯時,可以增加傳入的查詢時間范圍的開始時間過濾是否需要查詢冷庫,針對冷庫熱庫都存在情況時優先保留熱庫數據(只過濾同一分頁內的相同單號數據),如果對結果有異議可使用單號單獨再次查詢返回最新再次確認。跟產品和運營確認能不支持的就僅查詢熱庫。
更新
邏輯在數據庫交互層統一實現處理
插入
邏輯在數據庫交互層統一實現處
支付-無歸檔表(查詢、新增、更新)
特點: 唯一鍵都為內部單號,現有主要查詢可以根據單號判斷時間,不需要歸檔表,可以徹底解決熱庫數據庫存儲問題。
查詢
邏輯在數據庫交互層統一實現處理
存在以下部分情況可做特殊處理減少數據庫冷庫依賴。
單筆查詢:
- 根據支付單號查詢,如果可以根據單號判斷時間,查詢冷庫前使用單號進行時間范圍過濾。
批量查詢:
- 根據交易單號查詢,如果可以根據單號判斷時間,查詢冷庫前使用單號進行時間范圍過濾。
- 部分功能管理后臺功能分頁查詢,對數據查詢范圍要求較高的增加冷庫查詢邏輯時,可以增加傳入的查詢時間范圍的開始時間過濾是否需要查詢冷庫,針對冷庫熱庫都存在情況時優先保留熱庫數據(只過濾同一分頁內的相同單號數據),如果對結果有異議可使用單號單獨再次查詢返回最新再次確認。跟產品和運營確認能不支持的就僅查詢熱庫。
更新
邏輯在數據庫交互層統一實現處理
插入
邏輯在數據庫交互層統一實現處
總結
- 支付因沒有歸檔表徹底解決了數據庫存儲壓力問題,大大的節省了數據庫存儲資源。
- 交易因新增了歸檔表,大大延緩了熱庫數據庫存儲壓力,為交易數據庫又額外提供了緩沖擴容時間,為后續再次優化徹底交易解決數據庫存儲問題提供了充足的時間。
成果
- 徹底解決了支付數據庫存儲壓力問題,有效的緩解了交易數據庫熱庫的存儲壓力。
- 數據庫熱庫保留天數可靈活調控,可以根據后續訂單量進行合理調整存儲可用天數。
缺點
- 交易采用方案二新增了歸檔表,并且歸檔表里的存儲的全量數據,僅能減緩交易和支付數據庫的存儲空間緊張情況,無法徹底解決數據庫存儲問題。
- 交易表釋放的 datafree 存儲空間無法提供給歸檔表使用,僅能交易表使用,需要不定期釋放交易表的 datafree 空間。