高併發賬戶記錄查詢

語言: CN / TW / HK

問題描述

高併發賬戶記錄查詢在銀行、網際網路企業、通訊企業中廣泛存在。例如:網上銀行、手機銀行、電商個人賬戶查詢、互聯網遊戲賬戶等等。這類查詢有三個共同點:

1、 資料總量非常大。使用者數量本身就非常多,再加上多年的賬戶資料,資料量可以達到幾千萬甚至上億條。

2、 訪問人數眾多。幾百萬甚至上千萬人訪問,屬於高併發查詢。

3、 不能讓使用者等待。手機、網頁要達到秒級響應,否則嚴重影響使用者體驗。

下面以某銀行賬戶活期明細查詢為例,給出這類問題的解決辦法。

某銀行共一億個活期賬戶,每個賬戶平均每月有7條資料,每年資料總量84億條。每條資料中的機構欄位,還要關聯分支機構表(幾千條)記錄。在效能上,要求單臺伺服器支援一千個以上的查詢,響應時間不能超過1秒。

有序行存

活期明細資料隨著時間增長非常快,一年就有84億條。如果放到記憶體中,需要大量記憶體空間,硬體投入成本太高,所以要放到硬碟上儲存。分支機構表只有幾千條資料,可以放在記憶體中儲存。

在硬碟上儲存,要考慮是行存還是列存。列存資料分塊壓縮,能減少遍歷資料量。但由於賬戶查詢是隨機的,整塊讀取會有額外解壓計算。而且每次取數都針對整個分塊,複雜度較高,效能不如行存。因此,這個場景要選擇行存儲存,如下圖:

2870626391756be40be9dfc6e715c226.jpeg

圖1:行存和列存

具體的實現可以採用Java、C++、SPL等高階語言。這裡我們以程式碼量最少的SPL語言為例講解。

c83c0632024e3912a3af06dd676ae8c7.jpeg

程式碼示例1

A1:連線生產資料庫,用遊標讀取活期存款資料,按照賬戶id排序。

A2:建立本地組表文件。

A3:建立組表,並從資料庫遊標讀取活期存款明細資料,寫入檔案。

其中,A3中的@r選項,就是建立行存檔案。一年84億條資料都匯出,時間會比較長。但是這是一次性的工作,後續就只需要追加增量資料即可。增量資料的追加方法,後面會有介紹。如果按照賬戶排序會對生產資料庫造成較大壓力,可以匯出之後基於檔案排序。排序使用SPL的sortx函式,具體用法參見函式參考。

利用索引

要利用索引提速,先要對明細檔案建立索引。由於明細資料量大,建立的索引檔案也會很大。很難全部載入到記憶體中。可以建立多級索引,如下圖:

1d520f121a5db8451236b31583eabcb5.jpeg

圖2:多級快取

還是以SPL為例,建立多級索引,只需要在“程式碼示例1”的基礎上,增加一個網格即可:

df0c6685629a677fe72f64122c525d92.jpeg

程式碼示例2

A4:對行存檔案建立索引檔案。

載入的索引級別越多,佔用儲存空間越大。同時,賬戶id的跨度變小,載入到記憶體中後,索引效果也會變好。查詢時可以根據記憶體大小,儘可能載入更多級別的索引,可以有效提高查詢速度。

在查詢之前,系統初始化或者資料變動時,要預先載入多級索引,以SPL程式碼為例:

a0ab82a80da6ab127885aabca3a18ff3.jpeg


圖片.png

4268a107472fc027ef1d27aa35bc9a9b.jpeg

圖3:節點機自動呼叫初始化程式碼

由於我們提前準備好的活期資料是對賬戶id物理有序的,查詢時根據索引找到賬戶id後,可以在硬碟連續讀取,顯著減少磁碟IO,有效提速,如下圖:

321335767e2719fa4fa267e6f919d45e.jpeg

圖4:索引和有序行存

查詢賬戶10100,先利用記憶體中預先載入的多級索引和索引檔案,快速定位到活期明細資料檔案中的位置,再連續讀取到賬戶id有變化為止。因為資料是按照賬戶id物理有序的,所以檔案的其他位置不可能再有10100賬戶的資料了。

利用索引查詢的示例程式碼如下:

0dabfd4e7836d506c002372e1d881473.jpeg

程式碼示例4

A1:全程變數detailR已經快取了三級索引,現在可以基於它利用索引,按照條件取出賬戶為10100的記錄。

每個賬戶的資料量並不大,所以可以全部讀入記憶體。

活期明細前端應用(網頁或者APP),要通過集算器的JDBC驅動呼叫SPL程式碼查詢。呼叫的時候,需要將賬戶id作為引數傳給SPL程式。例如:定義一個網格引數countid,傳入賬戶id為10100。A1中的程式碼就要改為:=detailR.icursor (;ID==countid,index_detailR_id).fetch()。

關聯查詢

查到指定賬戶資料裝入記憶體後,可以將機構資料也讀入記憶體。在記憶體中關聯計算,效能可以得到保障。示例程式碼如下:

557164b0f59833b9023331941c3b1356.jpeg

程式碼示例5

A2:讀入機構資料。

A3:賬號10100的活期明細資料關聯機構資料。

A4:將賬戶id、分支機構名稱和金額返回給前端呼叫者(網頁或者APP)。

機構資料可以在應用系統初始化的時候載入入記憶體,不必每次讀取,查詢速度更快。在上面提到的init.dfx中增加程式碼如下:

7561df11fa54816df327d273e52640cc.jpeg

程式碼示例6

預先載入機構資料之後,查詢程式碼要在“程式碼示例5”的基礎上去掉載入機構資料的部分,修改之後的查詢程式碼如下:

2bbf0f8560d833ff4fd6099576f23063.jpeg

程式碼示例7

A2:直接用預先載入的全程變數corp和活期明細資料關聯計算,省去了每次查詢載入機構資料的時間。

資料更新

活期明細存款是按賬號有序的,並不是按日期有序。所以,不能在末尾追加當日新增資料。活期明細幾十億條,如果每天有序歸併新資料的話,耗時太長。每月歸併一次的方案比較理想。如果將活期明細資料看成是主檔案,那麼更新原理如下圖:

33a583b56b3e30cfd462d359d59a31b6.jpeg

圖5:資料更新

資料更新的示例程式碼如下:

2e6859e480c4418b5deffde2175d3d94.jpeg

程式碼示例8

A1、B1:如果是每月1日,重整檔案。

A2:從生產資料庫中讀入當日資料。

A3:將當日資料有序歸併到集算器自動生成的補檔案中。