摘要:预算管理要求消耗的物品的数量能够按照预算库存进行消耗,不能超额,尽可能的保障预算的消耗率。描述了用户执行抢购的交易信息订单号,抢购标的编号,状态冻结失败成功交易数量,用户编号。
题目
背景描述:
在现今电子商务场景下,预算管理在很多场景下都有广泛应用,比如营销奖品,积分、红包的发放、商品库存管理等。预算管理要求消耗的物品的数量能够按照预算库存进行消耗,不能超额,尽可能的保障预算的消耗率。
大规模请求量以及分布式环境下,预算管理问题更加突出,如果预算控制不合理,则会出现物品超发或是预算消耗率不高的情况,超发会导致直接的资金损失出现;预算消耗率不高则会导致商品无法卖出、奖品无法发放等问题,从而导致间接的资金损失。
根据上面的背景,用伪代码描述如下功能,在分布式集群环境下,有一批积分需要发放给用户,存放在数据库中,实现预算的扣减方法。
//: TODO 可自行定义变量 /** * 扣减预算 * @param consumption 当前需要扣减的消耗量 * @return 预算的扣减是否成功 */ public boolean budgetDeduct(int consumption){ //: TODO 完成此处的代码 }思考
作为新手,可能只会想到加锁,比如先加上悲观锁(先来个select for update,锁住之后再来个update 加上超发的where限制等)。所有的事情都在一个服务方法中完成,即可。好处是实现简单,坏处呢,比如响应慢,执行效率低(悲观锁)等。
如果是老手,实现就会比较复杂
标的物表 target_tbl(id,name,total_number,available_number,froze_number)
抢购交易表trade_order_tbl(order_id,target_tbl_id,state,trade_number,user_id)
target_tbl描述了抢购的目标物件信息:名字,总数量,可用数量,冻结数量。
trade_order_tbl描述了用户执行抢购的交易信息:订单号,抢购标的编号,状态(冻结/失败/成功),交易数量,用户编号。
public boolean budgetDeduct(int consumption){ Long tradeOrderId=frozeTarget(userId,targetId,consumption); if(tradeOrderId !=null){ //冻结成功之后,执行业务操作,或是检查 bool isBusiSucc = doBusiness(); //业务操作成功,则更新交易,并将抢购商品入个人账 if(isBusiSucc){ return updateTrade() }else{ //业务操作失败,则解冻交易 return undoFrozeTarget(); } }else{ //冻结不成功,返回失败 return false; } } /** * 需要事务支持,异常回滚 * @param userId 抢购用户编号 * @param targetId 抢购商品编号 * @param consumption 抢购数量 * @return **/ private Long frozeTarget(String userId,String targetId, int consumption){ int updated=0; //通过乐观锁的方式进行更新,尝试5次,5次仍旧失败,则认为冻结失败 for(int i=0;i<5;i++){ //update target_tbl //set froze_number = froze_number+consumption ,available_number = available_number-consumptionwhere available_number>=consumption updated = executeFroze(); if(updated != 0) { break; }else{ continue; } } if(update == 0){//冻结不成功 return null; }else{ //冻结成功,添加抢购交易记录 Long tradeOrderId = createOrder(); return tradeOrderId; } } /** *需要事务支持,异常回滚 *失败需要重试 */ private bool updateTrade(){ //1.更新交易表状态为成功 //2.更新标的表,使标的表的total_number,available_number,froze_number 减去交易表的trade_number //3.更新用户信息 } /** *需要事务支持,异常回滚 *失败需要重试 */ private bool undoFrozeTarget(){ //1.更新交易表状态为失败 //2.更新标的表,使标的表的available_number+trade_number,froze_number-trade_number }另一个思路
一般我们在设计的时候,流水表和商户账户表是同时更新的,比如用户充值,冲完之后,用户账户就是更新后的值。但是在一些业务场景下,流水表和账户表不需要同时更新,账户表通过定时任务来批量更新,或是业务上允许账户变动能容忍一定的滞后。这带了的思考是,先将完整业务流程细分到更新表的操作,再从业务上去梳理,将几个表的操作组合起来,重新组合为业务场景。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70432.html
摘要:接下来,我主要从三个阶段回顾我的秋招,分别是前期中期尾声。到了这里,我的秋招算是正式的尘埃落定了,签完三方后,我的秋招结束了。四复盘总结这次的复盘主要是我自己整个秋招的历程缩影,很多细节无法在一篇文章就说清楚。 ...
摘要:最近要招一名中高级程序员有位同学的面试表现我们几位面试官都觉得不错思维很活跃知识面也还挺广尤其是主动提及最近还在研究这个高性能框架并说出了基本实现原理表现出喜欢钻研技术的态度唯一的缺点就是逻辑思维不太缜密工作经验只有年多但我们都觉得稍加培养 最近要招一名中高级程序员, 有位同学的面试表现我们几位面试官都觉得不错: 思维很活跃, 知识面也还挺广, 尤其是主动提及最近还在研究Disrupt...
摘要:正如我标题所说,简历被拒。看了我简历之后说头条竞争激烈,我背景不够,点到为止。。三准备面试其实从三月份投递简历开始准备面试到四月份收,也不过个月的时间,但这都是建立在我过去一年的积累啊。 本文是 无精疯 同学投稿的面试经历 关注微信公众号:进击的java程序员K,即可获取最新BAT面试资料一份 在此感谢 无精疯 同学的分享 目录: 印象中的头条 面试背景 准备面试 ...
摘要:正如我标题所说,简历被拒。看了我简历之后说头条竞争激烈,我背景不够,点到为止。。三准备面试其实从三月份投递简历开始准备面试到四月份收,也不过个月的时间,但这都是建立在我过去一年的积累啊。 本文是 无精疯 同学投稿的面试经历 关注微信公众号:进击的java程序员K,即可获取最新BAT面试资料一份 在此感谢 无精疯 同学的分享目录:印象中的头条面试背景准备面试头条一面(Java+项目)头条...
阅读 2269·2021-11-23 10:09
阅读 2791·2021-10-12 10:11
阅读 2574·2021-09-29 09:35
阅读 1317·2019-08-30 15:53
阅读 2238·2019-08-30 11:15
阅读 2888·2019-08-29 13:01
阅读 2270·2019-08-28 18:15
阅读 3338·2019-08-26 12:13