项目中高并发抢红包
目录
高并发抢红包问题
红包随机值
为了避免抢红包时实时计算效率慢,采用创建红包活动时就提前计算入红包库。
分配红包尽可能平均,假如现剩60块钱,还要分成5个红包,平均是12,那么应该在0-24之间随机
简单示例代码:
// 分红包
func GenerateReadPackage(total int, num int) []int {
rand.Seed(time.Now().UnixNano())
packages := make([]int, num)
left := num
for i := 0; i < num; i++ {
if i == num-1 {
// 最后一个不用分
packages[i] = total
break
}
randInt := rand.Intn(total / left * 2)
fmt.Println(total, left, total/left, randInt)
total = total - randInt
left--
packages[i] = randInt
}
return packages
}
并发抢
红包表中有used字段表明是否已经被抢。
如果并发抢都查mysql,采用悲观锁造成大量用户等待,不可取。如果采用乐观锁,势必会造成大量用户更新红包used字段时失败,mysql数据库压力也大。
采用redis分布式锁可以减轻mysql压力,但是也会造成每次只有一个人拿到分布式锁后,才能去更新红包表中一个数据。
采用分段分布式锁可以减少对同一锁的压力。
我们在redis分成5个锁,lock1-5。另外还有5个list,list1-5,list元素为红包ID。 用户抢红包时根据用户ID的最后一位来判断要抢那个锁,[0-1]抢lock1,[2-3]抢lock2,以此类推。 当用户1拿到lock1后,取出list1中一个红包ID,去更新mysql红包状态处理后续工作。如果list1中元素没有了去数据库中一次取50个。红包表还有个pre_used字段表明已经被分配到redis list中
有种极端情况,redis中list1-5同时没有了,都去mysql取50个,并更新这50个pre_used为1,如果使用乐观锁,会造成只有一个成功,所以这里要用乐观锁再进行二次判断,更新状态时发现已经被别人取走,再取50条。