目录

项目中高并发抢红包

高并发抢红包问题

红包随机值

为了避免抢红包时实时计算效率慢,采用创建红包活动时就提前计算入红包库。

分配红包尽可能平均,假如现剩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条。