业务场景
对某个套餐或产品进行销售,设置待销售品总数量。销售时不能出现超卖情况,即销售品剩余数量不能为负数。
业务需求
系统高并发,极端热门套餐抢购比率只有1%,比如100个销售品在几秒内抢购完,在前端库存判断需要支持上10w的库存快速判断
商品不能出现超售情况
多个商户进行销售,保证剩余商品数量的数据一致性
需求分析
获取商品剩余数量进行条件判断和更新数据操作,是两个独立的操作,中间有可能有其他应用修改数据从而产生冲突。在冲突的发生时,用户需要合并最新的数据,再进行条件判断和数据修改。将两个独立操作封装成一个原子的服务,保证(剩余数量-销售量)之后,商品剩余量不为负数;
缓存提供了返回版本号的vget的操作和带版本号的数据减操作vdecrBy.
示例方案
方案1:先支持有超卖风险的原子事务操作
//没有冲突的减库存原子函数,在执行库存判断
public boolean decreaseFunction2(String key,long value, Jedis jedis,int retreyNumber){
Long remain = jedis.decrBy( key, 1);
return remain.longValue() > 0;
}
方案2:使用乐观锁客户端的乐观锁机制实现
注:完整缓存API使用代码可以参考并发修改冲突示例,本示例在于说明乐观锁的使用场景,在复杂的业务对象list,map等并发修改时更适用。
//没有冲突的减库存原子函数
public boolean decreaseFunction(String key,long value, VProxyJedis vjedis,int retreyNumber){
boolean flag = false;
//在扣除操作失败时,进行重试,重试次数为retreyNumber
while ( (!flag) && (retreyNumber>0) ){
try{
//原子操作1:通过vget得到当前key对应value的值和版本号
VersionResult result = vjedis.vget( key);
//判断剩余商品数量是否充足
if((Long.parseLong(result.getValue())-value)>=0){
//操作1 和操作2 之间可能有其他应用修改数据产生状态不一致
//原子操作2:商品剩余量-销售量,基于当前条件判断的版本号去更新
vjedis.vdecrBy( key, value, result.getVersoion());
}
//商品剩余数量扣除成功 , 设置操作成功标志flag = true
flag = true;
}catch(Exception e){
//如果有其他进程修改了数据,会引发版本号冲突,错误码:ErrorMessage_DiffVerison
if(e.getMessage().equals(ErrorMessage_DiffVerison)){
flag = false; //设置操作是否成功标志flag=false,
retreyNumber--;//减去一次重试次数,通过while循环再进行操作
}
}
}
return flag;
}
|