赞
踩
这两天做了一个抢红包的功能,这里对实现方法进行一个整理,整天的两个难点:1、如何应对红包的高并发。其实红包类似于秒杀这种功能,当一个红包从可以开始抢到全部被抢完,很可能不到一秒钟,所以性能上肯定不能有问题。2、红包的公平性,如何做到红包的相对公平以及防止出现10块钱红包10个人抢,第一个人直接抢了9.8导致后面的人可能不够分的情况。所以这里从这两个问题来展开。
因为这个主要是算法,比较单一,所以先从这个来。基本上分两个思路,一个是创建红包时,就把m金额的红包拆成n份存储起来,抢到时候直接根据当前的顺序获取对应槽位上的金额就好了。这种算法好处是一开始就确定了每一个人的金额,不会出现抢着抢着后面人不够抢的情况,坏处是创建红包时太慢,并且需要存储空间存储数据。
另一种思路就是用户抢的时候再去计算抢到了多少钱,基于这种思路,只要做到兼顾公平性和避免不够抢的情况,就比第一种思路要好。做之前看了网上的很多资料,基本都是基于第二种边抢边出金额的方式,最多的一种设计是基于一个二分法,比如100个人,10个红包,那平均每人应该是10元,所以第一个人的随机区间则是1到20,平均能抢10元。假设第一个人真的抢了10元,剩下90元和9个人,平均每人还是十元,第二个人的随机区间也是1到20,以此类推,所有人的数学期望其实都是10元,从而做到公平性。但是可能存在极端情况比如:
所以在这个二分随机算法的基础上进行改版,我给没人预留一块钱,然后对剩下的金额再进行二分随机获取,如下:
这样就避免了最后的人抢不到的情况,同时从数学上也保证了公平性。
前面说过抢红包类似于秒杀场景,需要一定的性能保证,并且同时得保证红包不会抢出问题,比如10个红包,因为并发没处理好造成抢出了20个红包,那就血亏了。基于这两个原因,传统的基于数据库对红包进行扣减,会造成较大的压力。并且如果我们基于数据库的悲观锁来实现时,有可能造成堵塞,而基于乐观锁实现,又容易造成大量的失败请求,影响用户体验。所以最终决定基于redis来进行实现,redis的lua脚本本身具备原子性,同一时间不会处理多笔抢红包事件,同时redis本身的高性能(秒级10w)的操作量也能很好的承接住这种秒杀场景。
基本的算法和设计方案敲定,再来推敲细节,redis的高性能得益于小操作,也就是你不能把一个很耗时的操作放到redis里(比如一个大循环),那可能会造成极为严重的影响,甚至堵塞住你的所有和redis相关的业务。所以我们这里仅仅将红包的扣减操作放到redis当中,设计如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。