帮助中心/最新通知

质量为本、客户为根、勇于拼搏、务实创新

< 返回文章列表

【服务器相关】Redis优惠券秒杀企业实战

发表时间:2025-09-24 16:09:00 小编:主机乐-Yutio

一、全局唯一ID

1. 全局ID生成器

每个店铺都可以发布优惠券:

当用户抢购时,就会生成订单并保存到tb_voucher_order这张表中,而订单表如果使用数据库自增ID就存在一些问题:

  • id的规律性太明显
  • 受单表数据量的限制

所以tb_voucher_order表的主键不能用自增ID:

为了增加ID的安全性,我们可以不直接使用Redis自增的数值,而是拼接一些其它信息:

D的组成部分:

  • 符号位:1bit,永远为0,表示正数
  • 时间戳:31bit,以秒为单位,可以使用69年
  • 序列号:32bit,秒内的计数器,支持每秒产生2^32个不同ID

编写全局ID生成器代码:

2. 全局唯一ID生成策略

  • UUID(不是递增的)
  • Redis自增
  • 雪花算法(snowflake)
  • 数据库自增(单独建一张表存自增id,分配到分库分表后的表中)

3. Redis自增ID策略

  • 以日期作为前缀的key,方便统计订单量
  • 自增ID的结构:时间戳 + 计数器

二、实现优惠券秒杀下单

1. 添加优惠券

每个店铺都可以发布优惠券,分为平价券和特价券。平价券可以任意购买,而特价券需要秒杀抢购:

优惠券表信息:

  • tb_voucher:优惠券的基本信息,优惠金额、使用规则等(tb_voucher表的type字段区分是普通券还是秒杀券)
  • tb_seckill_voucher:优惠券的库存、开始抢购时间,结束抢购时间(秒杀券才需要填写这些信息),同时秒杀券拥有普通券的基本信息(秒杀券表tb_seckill_voucher的主键id绑定的是普通券表tb_voucher的id)

测试结果:

三、实现秒杀下单

下单时需要判断两点:

  • 秒杀是否开始或结束,如果尚未开始或已经结束则无法下单
  • 库存是否充足,不足则无法下单

主要代码:

扣减库存成功:

四、超卖问题

当有大量请求同时访问时,就会出现超卖问题

超卖问题是典型的多线程安全问题,针对这一问题的常见解决方案就是加锁:

1. 加锁方式 – 乐观锁

乐观锁的关键是判断之前查询得到的数据是否有被修改过,常见的方式有两种:

(1)版本号法

(2)CAS法

  • 用库存代替了版本号,可以少加一个字段
  • 扣库存时,与查询时的库存比较,没被修改则可以扣减库存

2. 乐观锁解决超卖问题

乐观锁方式,通过CAS判断前后库存是否一致,解决超卖问题:

在扣减库存之前,加上一人一单的逻辑:

然后修改nginx的conf目录下的nginx.conf文件,配置反向代理和负载均衡:

修改完后,重新加载nginx配置文件:

现在,用户请求会在这两个节点上负载均衡,再次测试下是否存在线程安全问题:

访问8081端口的线程进入了synchronized中

访问8082端口的线程也进入了synchronized中

最终同一个用户下了2单扣了2个库存,所以在集群模式下,出现了一人多单的问题:

分析:

  • 锁的原理是每个JVM中都有一个Monitor作为锁对象,所以当对象相同时,获取的就是同一把锁
  • 但是不同的JVM中的Monitor不同,所以获取的不是同一把锁
  • 因此集群模式下,加synchronized锁也会出现并发安全问题,需要加分布式锁


联系我们
返回顶部