优惠券设计-获取随机码

来源:互联网 发布:360vr全景通源码 编辑:程序博客网 时间:2024/05/02 04:58

背景:
策划要求优惠券增加一个功能,需要将优惠券作为一个商品去购买,当优惠券为随机码券,用户下单时,从随机码表中取出一个随机码并绑定到对应的用户。相关表的关系如下:

  • 随机码表:包含随机码,优惠券id,使用状态等字段。
  • 优惠券表:包含优惠券使用条件和效果,领取数量,当优惠券类型为随机码券时,对应多个 随机码。
  • 用户优惠券关系表:包含用户id,优惠券id,状态等字段。

最初做法:当下单时,首先从随机码表中取出一个未使用的随机码,采用sql where条件为未使用并带上limit 1,然后进入一个事务方法,方法包含如下步骤:
1.标记获取到的随机码为已使用
2.增加优惠券表领取次数+1
3.插入用户优惠券关系表,其中码为获取的随机码

此做法看起来并未有什么大问题,也考虑了事务一致性。当并发量不大时,运行不会有什么问题,当并发量提升,由于采用 where条件加limit 1,这样可能导致多个用户同时获取同一个随机码,当更新获取到的随机码为已使用时,由于采用了乐观锁,此时只会有一个用户能更新成功,其余均失败。
这种做法的后果:并发量高时,部分用户会失败,这样将导致部分用户的订单白白流失。

由于自己也感觉这种方式不合适,翻阅了商城之前的一个购买第三方优惠码的做法,他的做法如下:
当下单时,首先获取未使用的优惠码100个,采用sql where带上条件未使用状态 limit 100,然后从这100个中随机取一个(采用Random)。其余类似,改进点在于为了减少碰撞冲突,一次取100个然后随机取一个,这样并发下单时,取到同一个的概率将会大大降低。但这种做法同样存在弊端。即当剩余码不多时,碰撞概率将大大提升,仍然会有部分用户失败。

自己想来想去总感觉不妥,于是咨询了下阿里过来的同事,他给出了如下建议,把获取优惠券码和后续操作放到同一个事务里面,具体做法如下,在一个事务中进行如下操作:
1.更新一个随机码已使用 (update 随机码表 set status=’已使用’ and userId=? where couponId=? limit 1)
2.如果更新成功,根据userId和couponId获取更新成功的随机码
后续操作相同。
这种方法的优点:能保证碰撞概率为0,最大限度的减少用户下单概率。

总结:
和同事沟通,其实这种有点类似秒杀的场景,就是有限的资源被多人来获取,需要保证先到先得,但秒杀更复杂,上面的方法是不适用的,一般遇到这种并发量不算高的先到先得情况,将获取和更新状态操作放到一个事务中,这样能避免并发问题,还有一种做法,就是随机码获取的时候生成,这样就能变获取为生成,直接插入即可,也可以避免上面的并发获取问题。

原创粉丝点击