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