摘要:起因及介紹在早期的賬戶系統中,但凡有賬戶變動,就會執行一次數據庫操作。這時,在一次處理過程中,合并同一個賬戶的所有操作,最后只提交一次,就能帶來很大的優化空間。根據業務需要,進行增減轉賬凍結解凍操作。
起因及介紹
在早期的賬戶系統中,但凡有賬戶變動,就會執行一次數據庫操作。這樣在有復雜一些業務操作的時候,例如單筆交易涉及多個用戶多個費用的資金劃撥,一個事務內操作數據庫幾十次也就大量的存在。而觀察這樣的場景,其本質可能只涉及少數幾方的賬戶。
這時,在一次處理過程中,合并同一個賬戶的所有操作,最后只提交一次,就能帶來很大的優化空間。
1. 初始化一個收集器ExecuteParam,用來存放有變動的賬戶、待新增的資金記錄、待處理的凍結數據和待新增的凍結記錄。
final ExecuteParam param = ExecuteParam.instance(); public class ExecuteParam { private final Mapcache = Maps.newHashMap(); private final List financeLogs = Lists.newArrayList(); private final Map freezeRecords = Maps.newHashMap(); private final List freezeHistorys = Lists.newArrayList(); public static ExecuteParam instance() { return new ExecuteParam(); } public Map getCache() { return cache; } public List getFinanceLogs() { return financeLogs; } public Map getFreezeRecords() { return freezeRecords; } public List getFreezeHistorys() { return freezeHistorys; } }
2. 根據業務需要,進行增、減、轉賬、凍結、解凍操作。
public interface FundTransactionService { /** 調增 */ void addCredit(TransactionCommandParam command, final ExecuteParam param); /** 調減 */ void addDebit(TransactionCommandParam command, final ExecuteParam param); /** 轉賬 */ void addTransfer(TransactionCommandParam command, final ExecuteParam param); /** 凍結 */ String addFreeze(TransactionCommandParam command, final ExecuteParam param); /** 解凍 */ BigDecimal addUnfreeze(TransactionCommandParam command, final ExecuteParam param); /** 更新DB */ void execute(String proofId, ExecuteParam param); } public static TransactionCommandParam createTransfer(...); public static TransactionCommandParam createFreeze(...); public static TransactionCommandParam createUnfreeze(...); public static TransactionCommandParam createCredit(...); public static TransactionCommandParam createDebit(...);
3. 所有資金操作在底層都按照:校驗操作類型->修改賬戶余額->資金記錄的流程執行
@Override public void addCredit(TransactionCommandParam command, final ExecuteParam param) { /** 1.校驗 */ /** 2.調賬 */ FinanceAccount receiverFa = credit(command.getReceiverOwnerId(), command.getReceiverRoleId(), command.getAmount(), param.getCache()); /** 3.資金記錄 */ param.getFinanceLogs().add(...); }
4. 其中修改賬戶余額的方法,會先嘗試從ExecuteParam中查找該賬戶是否已經被操作過,如果沒有才查詢一次DB。這樣就確保了同一個賬戶在一次處理過程中,無論有多少資金操作,只會查詢一次DB。
private FinanceAccount credit(Long ownerId, Long roleId, BigDecimal amount, Mapcache) { final String cacheKey = getCacheKey(ownerId, roleId); FinanceAccount fa = cache.get(cacheKey); if (fa == null) { // 此處只查詢一次DB fa = getFinanceAccount(ownerId, roleId); cache.put(cacheKey, fa); } // 調增: fa.credit(amount); return fa; }
5. 當所有業務操作完成之后,一次性提交本次處理過程中的所有賬戶
fundTransactionService.execute(proof.getProofId(), param); @Override public void execute(String proofId, ExecuteParam param) { /** FinanceAccount統一更新 */ for (FinanceAccount account : param.getCache().values()) { account.setProofId(proofId); // 熱點賬戶延遲更新 if (isHotAccount(account.getId())) { continue; } // DB update this.updateAccount(account); logger.info("賬戶更新[{}]", account); } /** FinanceLog統一批量記錄 */ financeLogDao.addFinanceLog(param.getFinanceLogs()); /** 凍結記錄統一批量更新 */ for (AccFundManagementRecord freezeRecord : param.getFreezeRecords().values()) { if (freezeRecord.getId() != null) { // DB update } else { // DB insert } logger.info(LoggerUtil.createInfoLog("execute","凍結記錄[{}]"), freezeRecord); } /** 凍結歷史統一批量更新 */ for (AccFundManagementHistory history : param.getFreezeHistorys()) { // DB insert } }總結和思考
這次優化不僅大幅減少了數據庫的負擔,而且也因為數據庫訪問次數少了,處理速度也快了(例如還款,原先的處理時間約為1到2s,優化后的處理時間約為40ms)。處理速度快了,使用樂觀鎖控制的并發異常也相應減少了。
另外值得思考的地方是,在第一步初始化收集器ExecuteParam的時候,將所有容器都創建出來了,并不是所有業務都會用到全部的容器,這里是否有必要?
我的想法是讓步于開發便利性。
誠然是可以根據不同的場景有選擇性的初始化相應的容器,但是這樣開發人員在使用的時候需要思考的更多,需要做選擇,不夠簡單明了。而且省去一兩個容器的初始化帶來的好處可以并不大。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77292.html
摘要:全局熱點賬戶平臺支出賬戶平臺收入賬戶過渡賬戶。。那么親,這和熱點賬戶沒關系,即使你查詢一個非常普通的賬戶,碰巧該賬戶同時在更新,你也查不準。。 問題描述 在某一瞬間,單個賬戶集中的發生資金變動,若不加控制,其賬戶余額會因發生臟讀、覆蓋更新等情況而錯誤記錄。如果簡單的以悲觀鎖、樂觀鎖的方式限制,雖然不會發生數據錯誤,但會造成服務不可用(該賬戶的更新請求全部失敗)。而請求重試、再次網絡通信...
摘要:這允許開發人員以邏輯區間建立并提交變動,以防止當部分提交成功時出現的問題納入版本控管的元數據每一個文件與目錄都附有一組屬性關鍵字并和屬性值相關聯。 代碼管理 Git...
閱讀 3560·2021-09-22 10:52
閱讀 1588·2021-09-09 09:34
閱讀 1990·2021-09-09 09:33
閱讀 758·2019-08-30 15:54
閱讀 2596·2019-08-29 11:15
閱讀 713·2019-08-26 13:37
閱讀 1667·2019-08-26 12:11
閱讀 2975·2019-08-26 12:00