蔡培茂,張申勇,譚忠兵,魏志軍
(北京理工大學(xué)珠海學(xué)院,珠海519088)
賬戶歷史交易數(shù)據(jù)查詢服務(wù)是銀行金融業(yè)務(wù)的重要組成部分,主要是指對客戶的銀行卡(借記卡、信用卡等)或者銀行活期存折,在一段時間內(nèi)發(fā)生存取款業(yè)務(wù)交易清單的檢索。賬戶歷史交易數(shù)據(jù)查詢是銀行信息化系統(tǒng)的不可缺少的功能,也是銀行客戶最常使用的功能之一,對本人交易歷史全貌了解和掌握是銀行客戶的強烈需求。
對于賬戶歷史交易數(shù)據(jù)查詢功能,業(yè)內(nèi)傳統(tǒng)解決方案是基于關(guān)系型數(shù)據(jù)庫的,一般采取主機分流做法,即事務(wù)型處理和分析型處理分離[1],也就是事務(wù)型業(yè)務(wù)處理由核心業(yè)務(wù)系統(tǒng)的關(guān)系型數(shù)據(jù)庫來承擔(dān),再建立另外的一個關(guān)系型數(shù)據(jù)庫來專門實現(xiàn)歷史數(shù)據(jù)存儲與查詢,但在傳統(tǒng)解決方案中,由于關(guān)系型數(shù)據(jù)庫的局限,查詢庫中僅提供最近幾年的客戶交易明細,單次查詢時間段跨度最長為一年左右,其余的交易歷史數(shù)據(jù)多以光盤庫、磁帶庫的形式存放,這樣的做法查詢效率低且成本高,一般用戶只能查詢近期數(shù)據(jù),不能對歷史數(shù)據(jù)全貌進行方便查詢,并且查詢條件單一,對多條件高級查詢支持不足。近年來,隨著各種便捷支付方式的普及,賬戶交易數(shù)據(jù)量呈現(xiàn)爆炸性增長,同時,客戶交易數(shù)據(jù)類型也在快速變化,出現(xiàn)手機銀行、網(wǎng)上銀行、微信銀行等不同使用方式,為了能夠以較高的性能滿足海量交易數(shù)據(jù)的查詢需求,常常需要采取分庫分表方式或采用分布式關(guān)系型數(shù)據(jù)庫來解決[2],但這些方式部署、管理、配置復(fù)雜,擴容困難,并且造成開發(fā)難度大幅上升。由于傳統(tǒng)關(guān)系數(shù)據(jù)庫可擴展性差、數(shù)據(jù)模型死板、嚴格事務(wù)機制等原因[3],無法滿足數(shù)據(jù)量巨大、高并發(fā)、高擴展和高可用的銀行海量歷史交易數(shù)據(jù)查詢的需求[4]。
近年來,為了獲得靈活的數(shù)據(jù)模型、分布式和橫向擴展能力,各種新型的、非關(guān)系型數(shù)據(jù)庫應(yīng)運而生,這些數(shù)據(jù)庫統(tǒng)稱為NoSQL,NoSQL數(shù)據(jù)庫類型多樣,包括Redis、HBase、MongoDB等,這些NoSQL數(shù)據(jù)庫可滿足不同場景的應(yīng)用需求[5]。為了更好地滿足客戶和監(jiān)管機構(gòu)等對于歷史交易數(shù)據(jù)的查詢要求,提高業(yè)務(wù)處理能力,改進客戶體驗,本文提出一種基于MongoDB的存儲和查詢銀行海量歷史交易數(shù)據(jù)的應(yīng)用解決方案,實現(xiàn)銀行海量歷史交易數(shù)據(jù)的全量存儲、多條件復(fù)雜查詢、高效在線統(tǒng)計計算和系統(tǒng)的可擴展、高可用等。
MongoDB是一個基于分布式文件存儲的開源NoSQL文檔數(shù)據(jù)庫,由C++語言編寫[6]。根據(jù)DB-En?gines發(fā)布的2021年2月份的數(shù)據(jù)庫排名中,MongoDB排名第五,并且分數(shù)保持持續(xù)增長。
MongoDB具有高性能、高可用、易橫向擴展、模式靈活、支持索引、支持聚合操作、支持動態(tài)查詢等優(yōu)勢,文件存儲格式是BSON格式,BSON是類JSON的一種二進制形式,支持內(nèi)嵌的文檔對象和數(shù)組對象。而關(guān)系型數(shù)據(jù)庫基于單機架構(gòu),模式固定,不容易橫向擴展,難以支撐系統(tǒng)所需要的海量數(shù)據(jù)。
銀行交易數(shù)據(jù)具有規(guī)模大、增長速度快、結(jié)構(gòu)易變等特點,查詢要求高可用、高性能和查詢條件復(fù)雜等,MongoDB的特點能夠很好地滿足銀行歷史交易數(shù)據(jù)存儲和查詢的需求,具體如下:
(1)高可用性。MongoDB提供副本集群架構(gòu),副本集中一組MongoDB實例保持相同數(shù)據(jù)集。通過部署副本集群,實現(xiàn)歷史交易數(shù)據(jù)存儲和查詢系統(tǒng)的自動恢復(fù)、故障自動接管、提供7×24小時可用等。
(2)高擴展性。MongoDB提供分片集群架構(gòu),分片是指數(shù)據(jù)拆分并分散存放在不同機器上,為MongoDB實現(xiàn)橫向擴展能力。通過部署分片集群實現(xiàn)歷史交易數(shù)據(jù)的全量存儲和檢索,使客戶能夠看到其交易歷史全貌,能夠?qū)崿F(xiàn)系統(tǒng)的平滑擴容。
(3)模式靈活。MongoDB提供靈活的BSON文檔模型,無須預(yù)定義模式結(jié)構(gòu),文檔中的鍵和值無須定義固定的類型和大小,非常適合交易數(shù)據(jù)結(jié)構(gòu)易變的特點,方便支持新的支付方式,使開發(fā)能夠快速迭代以響應(yīng)業(yè)務(wù)需求的變化。
(4)高性能。MongoDB提供了多樣性的索引支持和多種分片策略。通過設(shè)計合理索引和分片鍵、副本集多節(jié)點數(shù)據(jù)讀取等方法,可以實現(xiàn)歷史交易數(shù)據(jù)的高并發(fā)訪問和快速響應(yīng)。
(5)復(fù)雜查詢。MongoDB支持豐富的查詢語言、數(shù)據(jù)聚合框架等,能夠滿足歷史交易數(shù)據(jù)多條件復(fù)雜查詢的需要,這也是MongoDB相對其他NoSQL數(shù)據(jù)庫的優(yōu)勢,如:HBase。
交易數(shù)據(jù)查詢具有大量高并發(fā)讀操作的特征,傳統(tǒng)主機架構(gòu)性能擴展不易、成本高,為了降低核心業(yè)務(wù)系統(tǒng)的壓力,業(yè)界普通采用主機分流。根據(jù)對Mon?goDB特點的分析,這里采用MongoDB作為主機分流數(shù)據(jù)庫,將業(yè)務(wù)數(shù)據(jù)從現(xiàn)有主機或Oracle等關(guān)系型數(shù)據(jù)庫實時同步復(fù)制到MongoDB,以實現(xiàn)客戶對交易數(shù)據(jù)查詢或統(tǒng)計分析,系統(tǒng)架構(gòu)如圖1所示。
圖1 系統(tǒng)架構(gòu)
圖1 系統(tǒng)架構(gòu)各組成部分說明如下:
(1)核心業(yè)務(wù)系統(tǒng)。該系統(tǒng)是現(xiàn)有銀行核心業(yè)務(wù)系統(tǒng),承擔(dān)銀行核心交易事務(wù)處理,交易數(shù)據(jù)存儲在關(guān)系型數(shù)據(jù)庫中,包括賬戶交易明細、客戶回單信息、賬戶基本信息、客戶基本信息等最新數(shù)據(jù)。
(2)采集層。該層實現(xiàn)從核心業(yè)務(wù)系統(tǒng)的異構(gòu)數(shù)據(jù)庫中實時同步數(shù)據(jù)到MongoDB中,這里使用Kafka實時監(jiān)聽核心業(yè)務(wù)系統(tǒng)中數(shù)據(jù)變動并同步至Mon?goDB。Kafka是由Apache軟件基金會開發(fā)的一個開源的、高吞吐量的分布式發(fā)布訂閱消息系統(tǒng)[7],非常適合實時數(shù)據(jù)的傳輸。采集層也可以采用市場上一些專門針對MongoDB的同步數(shù)據(jù)產(chǎn)品。
(3)數(shù)據(jù)層。該層采用MongoDB的副本集群、分片集群來存儲海量歷史交易數(shù)據(jù),以實現(xiàn)高可用、高性能、高擴展等系統(tǒng)特性。MongoDB集群架構(gòu)如圖2所示。
(4)業(yè)務(wù)層。該層是查詢業(yè)務(wù)的實現(xiàn)層,一般采取分層架構(gòu)以實現(xiàn)系統(tǒng)功能的低耦合、高內(nèi)聚的設(shè)計要求,如:數(shù)據(jù)訪問層的職責(zé)是通過MongoDB驅(qū)動程序訪問MongoDB中的數(shù)據(jù);邏輯層的職責(zé)是根據(jù)客戶請求實現(xiàn)業(yè)務(wù)邏輯的處理;控制層職責(zé)是適配不同客戶端,實現(xiàn)請求流程控制與跳轉(zhuǎn)。
(5)客戶端。用戶可以通過不同方式訪問系統(tǒng)功能,如:手機App、網(wǎng)上銀行、微信小程序、柜員機、銀行內(nèi)部系統(tǒng)等。
在系統(tǒng)架構(gòu)中,數(shù)據(jù)層中MongoDB的部署模式是需要重點考慮的部分,為保證生產(chǎn)過程的高可靠和高可擴展,需要結(jié)合MongoDB副本集和分片機制來搭建MongoDB的分布式集群架構(gòu),如圖2所示。
圖2 MongoDB集群架構(gòu)
為了實現(xiàn)交易數(shù)據(jù)查詢系統(tǒng)的自動容錯、自動故障恢復(fù)[8]和高可用,需要部署MongoDB副本集群,一般典型的副本集由3個以上具有投票權(quán)的節(jié)點組成,由于副本集中的節(jié)點都有可能成為主節(jié)點,所以副本集中的節(jié)點的硬件配置要保持一致。副本集除了實現(xiàn)高可用外,還有以下附加作用,如數(shù)據(jù)分發(fā)、讀寫分離和異地容災(zāi)等,所以副本集中成員節(jié)點的數(shù)量需要根據(jù)系統(tǒng)實際需求進行增減,但不能少于3個節(jié)點。
分片集群可以有效解決性能和擴容問題,但是分片需要更多的機器,管理復(fù)雜,因此在數(shù)據(jù)規(guī)模不大的情況下,使用副本集的方案就夠了[9],不需要使用分片集群,然而,隨著時間的推移和各類便捷的支付方式的普及,會出現(xiàn)數(shù)據(jù)量快速增長、訪問性能下降等問題,同時,為了實現(xiàn)歷史交易數(shù)據(jù)的全量存儲和檢索目標,最終是需要分片集群來解決的。分片群集的規(guī)模需要根據(jù)所需存儲數(shù)據(jù)總量、并發(fā)量總數(shù)、緩存數(shù)據(jù)大小等因素來綜合規(guī)劃和確定。
為了提高路由的可用性和實現(xiàn)負載均衡,路由節(jié)點至少2個,可根據(jù)實際并發(fā)訪問情況增加更多的路由服務(wù)器節(jié)點。
配置服務(wù)器存儲了分片集群的元數(shù)據(jù),為了保證高可用,所以配置服務(wù)器同樣必須部署成副本集。
銀行業(yè)務(wù)中存在各種不同類型的賬戶,如:借記卡、信用卡、股票賬戶、基金賬戶等,每種賬戶的交易數(shù)據(jù)類型也不相同,并且隨著業(yè)務(wù)的開展,還會出現(xiàn)新的賬戶類型和新的交易數(shù)據(jù)類型,因此存儲交易數(shù)據(jù)的數(shù)據(jù)模型必須靈活和支持彈性擴展,以適應(yīng)新業(yè)務(wù)發(fā)展需要。MongoDB是無模式數(shù)據(jù)庫,提供非常靈活的文檔數(shù)據(jù)模型,完全能滿足銀行業(yè)務(wù)的這種需求。MongoDB的BSON文檔是一種模式自由的存儲形式,能夠動態(tài)地添加數(shù)據(jù)項,靈活性很高。
交易數(shù)據(jù)大多數(shù)情況下涉及客戶信息、賬戶信息和交易明細三類。為了獲得高效率的查詢,設(shè)計兩個集合來存儲主要數(shù)據(jù):客戶賬戶集合customer和交易明細集合txdetail。
客戶賬戶集合customer的文檔格式示例如下:
交易明細集合txdetail的文檔格式示例如下:
以上文檔示例僅包括交易數(shù)據(jù)中最常用的基本信息,對于單據(jù)、證件影像數(shù)據(jù),由于一般查詢不會用到這些數(shù)據(jù),因此應(yīng)該設(shè)計單獨的集合來存儲,同時考慮在MongoDB中BSON文檔有16MB大小的限制,所以,需要保存到MongoDB的GridFS中[10]。
在基于MongoDB數(shù)據(jù)庫的系統(tǒng)中,索引和分片鍵是非常關(guān)鍵的,下面結(jié)合銀行海量歷史交易數(shù)據(jù)查詢的應(yīng)用特點給出主要索引和分片鍵的設(shè)計策略。
為了避免全集合掃描,提高查詢性能,MongoDB提供了索引機制。在交易數(shù)據(jù)查詢中,最常見的查詢場景是根據(jù)賬戶編號、交易時間范圍、交易金額范圍等多個條件組合來查詢交易明細集合txdetail,并且根據(jù)交易時間倒序排列,所以需要創(chuàng)建一個包含這些查詢條件字段的組合索引。
MongoDB索引采用B-樹結(jié)構(gòu),因此組合索引中的索引字段順序非常重要,組合索引中索引字段的最佳排序方式是精確匹配的字段在前面、排序條件字段在中間,范圍匹配的字段在最后。對于交易數(shù)據(jù)查詢,賬戶編號是精確匹配、交易時間和交易金額是范圍匹配、而交易時間常用于排序,所以對交易明細集合txdetail創(chuàng)建這個主要索引:db.txdetail.createIndex({account_no:1,tx_date:-1,tx_amount:1})。
為了存儲和處理海量歷史交易數(shù)據(jù),需要借助MongoDB分片機制來實現(xiàn)。在MongoDB的分片集群中,分片鍵的設(shè)計是非常關(guān)鍵的,分片鍵決定了數(shù)據(jù)分布方式,直接影響集群的性能。設(shè)計分片鍵時需要綜合考慮分片鍵的基數(shù)、分片鍵值分布、定向訪問等因素,通常選擇基數(shù)大、數(shù)值分布均勻、對主要查詢具有定向能力的一個或多個字段組合來作為分片鍵。
交易明細集合txdetail會隨著時間的推移,數(shù)據(jù)量會快速增長,達到非常大的規(guī)模,因此該集合需要進行分片。同時,大部分場景是根據(jù)賬戶編號、交易時間范圍來查詢交易數(shù)據(jù)。本系統(tǒng)的分片鍵設(shè)計應(yīng)達到“分散寫”和“集中讀”的目標,從而提高系統(tǒng)性能?!胺稚憽笔侵笍暮诵臉I(yè)務(wù)系統(tǒng)同步數(shù)據(jù)至MongoDB集群時,數(shù)據(jù)能夠均勻地分布到集群中各分片服務(wù)器中,避免因總寫某一分片服務(wù)器和某一分塊而導(dǎo)致出現(xiàn)熱點問題和超大塊問題;“集中讀”是指同一個賬戶的交易數(shù)據(jù)總是在同一個分片服務(wù)器中,查詢時能夠直接定向到該分片服務(wù)器進行數(shù)據(jù)讀取,而不需要將查詢分發(fā)到不同的分片服務(wù)器。
綜上所述,對交易明細集合txdetail采用“交易賬戶編號+交易時間”作為分片鍵,“交易賬戶編號”在前面能實現(xiàn)“集中讀”的目標,而加上“交易時間”能夠增大分片鍵值基數(shù),避免出現(xiàn)超大塊問題,實現(xiàn)“分散寫”的目標,操作命令如下:
對數(shù)據(jù)庫啟用分片:sh.enableSharding("數(shù)據(jù)庫名")
對交易明細集合txdetail建立索引(前面已創(chuàng)建):
db.txdetail.createIndex({account_no:1,tx_date:-1})
對交易明細集合txdetail建立分片:sh.shardCollec?tion("數(shù)據(jù)庫名.txdetail",{account_no:1,tx_date:1})
MongoDB提供多種語言的驅(qū)動程序API,如:Java、Python等,可以采用這些語言來實現(xiàn)業(yè)務(wù)層,以下給出的Mongo Shell操作指令,可以很容易轉(zhuǎn)化為相應(yīng)開發(fā)語言API的調(diào)用。
圖3 展示了歷史交易數(shù)據(jù)查詢的常見查詢條件,其中基于賬戶、交易時間范圍是最常用的查詢,該查詢的Mongo Shell操作指令如下:
圖3 查詢條件
圖4 展示了歷史交易數(shù)據(jù)查詢結(jié)果,其中本頁統(tǒng)計交由客戶端實現(xiàn),本次查詢收支金額總計則由Mon?goDB的聚合框架來實現(xiàn),具體Mongo Shell操作指令如下:
圖4 查詢結(jié)果
本文給出了基于MongoDB實現(xiàn)銀行海量歷史交易數(shù)據(jù)查詢的解決方案,通過對歷史交易數(shù)據(jù)查詢業(yè)務(wù)分析,結(jié)合MongoDB高可用、高性能、高擴展和模式靈活等特點,設(shè)計了系統(tǒng)架構(gòu)、數(shù)據(jù)模型、合適的索引、分片鍵和系統(tǒng)實現(xiàn)效果。該方案能夠?qū)崿F(xiàn)銀行交易數(shù)據(jù)查詢能力從1年提升至10年或甚至全量數(shù)據(jù),實現(xiàn)毫秒級快速響應(yīng),大大提升用戶體驗,并能夠節(jié)省大量系統(tǒng)維護時間,提供7×24持續(xù)服務(wù)。另外,通過Mon?goDB存儲了全量的客戶信息和交易明細,為銀行實現(xiàn)業(yè)務(wù)創(chuàng)新和為客戶提供個性化服務(wù)奠定了基礎(chǔ)。