抢红包(多个小红包、总金额固定红包),用户投资收益结算三个场景的技术解决方案思路
第一个场景,p2p公司在每天的1点,3点,5点整把标的放出来,一个标的200万金额,每个用户都可以抢随机金额投资,比如可以买5千,买1万等,抢红包的人数高峰期有30万,这个标的不限制抢购人数,直到这个标的被抢完为止,要防止并发,又要扛得住用户数,这个场景用什么方案
第二个场景,p2p公司搞活动抢红包,红包总金额5千万,分成100万个小红包,每个小红包金额随机,每个用户可以抢多个红包,同时抢红包的用户数量有30万,如何解决并发的问题
第二个场景,p2p公司搞活动抢红包,红包总金额5千万,分成100万个小红包,每个小红包金额随机,每个用户可以抢多个红包,同时抢红包的用户数量有30万,如何解决并发的问题
第三个场景,分布式计算收益,类似于余额宝,每天需要用Job定时去给每个投资用户做收益统计,总投资用户数有5千万,为了提高可靠性和加快计算时间,需要开多台Job服务器分批对用户进行收益计算,每个用户只能计算一次收益,不能被多台Job服务器重复计算,如果某些用户的收益计算失败或一定时间内没有计算完成,需要有补偿机制,这个方案如何设计
解决思路:
红包业务两个场景的方案
第一个场景解决方案,用一个固定金额的redis key把余额存起来,然后对余额做incr操作,拿到剩余数据大于等于0的可以做下单业务。
第一个场景解决方案,用一个固定金额的redis key把余额存起来,然后对余额做incr操作,拿到剩余数据大于等于0的可以做下单业务。
第二个场景解决方案,用1000万个redis队列,出列成功可以做抢红包业务。
第三个场景解决方案
分布式收益结算处理方案
1.用定时任务中心将要结算收益的用户发送到MQ队列中,用户数据从数据库拉取,拉取的时候按userId升序排序
2.将用户数据推送到MQ后,要记录最后入列的userId,任务中断时,重启任务后可以从最后入列成功的userId处继续入列MQ
3.MQ出列的时候,获取一个得到任务的Redis锁
4.每天一个收益表,用userId作为主键插入一条统计数据,做幂等处理
5.验证数据库的收益计算状态,做幂等处理,防止重复计算收益
6.业务处理
7.业务处理成功需要更新状态
8.消息通知业务
9.补偿计算收益,定时Job对比基础users表与userStat表的数据,users表存在但userStat表不存在,说明漏掉了计算收益,
userStat表的状态不等于2,说明收益处理失败,都需要重新计算
出列
onConsumer((message) =>{
if(redisCache.Add(得到任务_@userId,1,When.NotExist, 30秒过期) == false)
{
return;
}
//userId设为主键,用主键约束一条数据只能统计一次
if(insert usersStat_20190816(userId,createTime,state,amount,income) values(@userId,now(),0,@amount,0) == 0)
{
return;
}
var data = select * from usersStat where userId=@userId and state=0;
//数据库要验证状态,因为用redis集群可能会有多个线程同时拿到锁
if(data != null && (update usersStat_20190816 set state=1 where userId=@userId and state=0) >=1)
{
//处理业务
//处理完成回写表
var rows = update usersStat_20190816 set state=2,income=@income where userId=@userId and state=1;
if(rows >= 1)
{
//处理成功,做其它非收益功能业务,比如消息通知,消息通知用MQ解藕
}
else{
//重复计算收益了,回滚
}
}
});
1.用定时任务中心将要结算收益的用户发送到MQ队列中,用户数据从数据库拉取,拉取的时候按userId升序排序
2.将用户数据推送到MQ后,要记录最后入列的userId,任务中断时,重启任务后可以从最后入列成功的userId处继续入列MQ
3.MQ出列的时候,获取一个得到任务的Redis锁
4.每天一个收益表,用userId作为主键插入一条统计数据,做幂等处理
5.验证数据库的收益计算状态,做幂等处理,防止重复计算收益
6.业务处理
7.业务处理成功需要更新状态
8.消息通知业务
9.补偿计算收益,定时Job对比基础users表与userStat表的数据,users表存在但userStat表不存在,说明漏掉了计算收益,
userStat表的状态不等于2,说明收益处理失败,都需要重新计算
出列
onConsumer((message) =>{
if(redisCache.Add(得到任务_@userId,1,When.NotExist, 30秒过期) == false)
{
return;
}
//userId设为主键,用主键约束一条数据只能统计一次
if(insert usersStat_20190816(userId,createTime,state,amount,income) values(@userId,now(),0,@amount,0) == 0)
{
return;
}
var data = select * from usersStat where userId=@userId and state=0;
//数据库要验证状态,因为用redis集群可能会有多个线程同时拿到锁
if(data != null && (update usersStat_20190816 set state=1 where userId=@userId and state=0) >=1)
{
//处理业务
//处理完成回写表
var rows = update usersStat_20190816 set state=2,income=@income where userId=@userId and state=1;
if(rows >= 1)
{
//处理成功,做其它非收益功能业务,比如消息通知,消息通知用MQ解藕
}
else{
//重复计算收益了,回滚
}
}
});