题目
背景描述:
在现今电子商务场景下,预算管理在很多场景下都有广泛应用,比如营销奖品,积分、红包的发放、商品库存管理等。预算管理要求消耗的物品的数量能够按照预算库存进行消耗,不能超额,尽可能的保障预算的消耗率。
大规模请求量以及分布式环境下,预算管理问题更加突出,如果预算控制不合理,则会出现物品超发或是预算消耗率不高的情况,超发会导致直接的资金损失出现;预算消耗率不高则会导致商品无法卖出、奖品无法发放等问题,从而导致间接的资金损失。
编程要求:
根据上面的背景,用伪代码描述如下功能,在分布式集群环境下,有一批积分需要发放给用户,存放在数据库中,实现预算的扣减方法。
//: 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
}
另一个思路
一般我们在设计的时候,流水表和商户账户表是同时更新的,比如用户充值,冲完之后,用户账户就是更新后的值。但是在一些业务场景下,流水表和账户表不需要同时更新,账户表通过定时任务来批量更新,或是业务上允许账户变动能容忍一定的滞后。这带了的思考是,先将完整业务流程细分到更新表的操作,再从业务上去梳理,将几个表的操作组合起来,重新组合为业务场景。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。