支付系統對賬算法優化方案 轉 http://www.yeeach.com/post/1318#comment-152796
一、目前對賬的算法:
1、從上游渠道(銀行、銀聯等金融機構)獲取對賬文件,程序逐行解析入庫
2、在存儲過程中,以上游對賬文件的表為基准,程序逐行讀取並與我們系統的交易記錄/賬務記錄(有賬務系統情況下,合理方案應該是於賬務記錄)對比,查找出差異記錄。
3、以我們系統的交易記錄/賬務記錄為基准,程序逐行讀取與上游對賬文件對比,查找出差異記錄
二、目前對賬算法問題:
1、使用存儲過程,對賬過程都在數據庫端完成,對數據庫性能影響較大,而且對賬邏輯擴展極為麻煩
2、逐行比對算法效率較低,但算法上並無好的優化余地。如果采用數據庫INTERSECT、MINUS對數據庫壓力也高。
3、在業務量大的情況下(例如有上百家上游渠道需要對,每一家都有幾十萬條交易記錄),對賬服務器及數據庫服務器負荷較高。即便采用讀寫分離,對賬時候使用讀庫,壓力一樣很大。
4、導入批量文件,逐行入庫效率較為低下(每一次都需要建立網絡連接、關閉連接)
三、對賬算法優化思路:
1、涉及網絡傳輸的,盡量采用批量方式操作,減少網絡消耗及時間消耗
2、使用Redis等NOSQL數據庫,降低數據庫服務器的壓力。同時在擴展上也容易,一台Redis服務器不夠,可以無限制增加用於對賬用的服務器。
3、使用Redis的set集合的sdiff功能,利用Redis sdiff算法的高性能,比對上游記錄和我方記錄的差異
四、對賬算法說明:
1、利用Oracle/Mysql的load data infile將對賬文件批量導入到數據庫
2、程序讀取上游賬務記錄表,對上游賬戶記錄執行select concat(channel_id, ":" , order_id , ":" , HASH_MD5(channel_id , order_id , amount , status , timestamp1 , timestamp2 ,…) ) as hashid from table ,得到對應的SET集合。
這里思路是將需要對賬的記錄拼成格式為:channel_id:order_id:hashid形式的串,以便作為Redis SET集合的item。
其中channel_id和order_id用於標識對應的渠道及訂單,只是例子,根據實際需要補充。之所以要在hashid前面帶上channel_id和order_id,主要目的是在sdiff后,能夠作為主鍵,根據set結合的item查找出對應的數據庫記錄。
3、對我方的交易記錄表/賬戶歷史表采用上一步的類似算法,得到對應的SET結合
4、采用Redis的pipeline功能(Redis的各種客戶端,包括java客戶端jedis 都提供此功能),將上游賬務記錄SET集合和我方的交易記錄/賬務歷史記錄SET集分別批量執行sadd插入Redis,得到兩個SET集合。
由於單條記錄sadd 插入Redis效率較差(每一次都涉及網絡open、close、傳輸消耗),因此使用Redis的pipeline功能,已實現批量入庫功能。
可以參考:http://redis.io/topics/mass-insert
5、利用redis的sdiff功能查找出上游賬務集合與我方記錄集合的差值
6、從sdiff 集合中channel_id:order_id:hash,定位對應的數據庫記錄,更新對賬狀態。
具體性能及實現可以在實現后測試對比一下,應該會有大幅的性能提升。