前言
关于抢红包服务一次数据库行锁控制并发,在此记录一下,或许里面还存在很多不足。
场景
抢红包应用:一个红包分为100个子红包,可以有N个人来抢这个红包,这时主要保证当有大于100个人来同时抢到这个红包,只能前100名抢到,并且所分配的红包金额不能大于发放红包金额。
1、发放红包
发红包过程比较简单,不涉及并发,这里简单说明。
以上为发放红包简单流程。
主代码生成主红记录,记录红包
insert into main_package(total_amount,remain_amout,total_num,remain_num)
2、抢红包流程
这里抢红包主要是控制并发,这里用了数据库行锁来控制伪代码如下:
int tryCount=0;
//加while循环,在发生并发时 失失败的情况可以重试,并且重试次数不大于10,避免数据库死锁,导致系统死循环
while(true && tryCount<10){
tryCount ++;
MainPackage mainPackage = select remain_amout,remain_num from main_package where orderid='orderid';
//红包余额小于等于0,红包已被抢光,返回
if(null == mainPacage || mainPackage.remainAmout<=0){
return false;
}
//生成随机金额
int randomAmount = getRandomAmount(mainPackage.remainAmout,mainPackage.remainNum);
//防止随机金额生成错误
if(randomAmount<=0){
return false;
}
//更新主红包剩余金额及剩余个数,这里是利用 where条件里面加上红包余额来控制并发的
int updateStatus = update main_package set remain_amout = mainPacage.remainAmout-randomAmount,
remain_num = remain_num-1
where orderid='orderid' and remain_amout= mainPacage.remainAmout;
if(updateStatus>0){
//生成子红包,这里没有控制事物,子表手机号作为唯一索引防止并发重复抢红包。
try{
instert into sub_package(orderid,sub_orderid,amount,mobile );
}catch{
//如果插入失败,将主表扣去的金额加回去
try{
update main_package
set REMAININGAMOUNT = REMAININGAMOUNT + #{redSubAmt}, REMAININGNUMBER = REMAININGNUMBER+1, where ORDERID = #{orderId}
}catch(){
return;
}
}
break;
}
}
我们做了并发测试,在qps可达到3000