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