978 字
5 分钟
Redis07-秒杀
2023-07-04
2023-11-25
无标签

全局唯一ID#

一般的订单会出什么问题呢?

第一点:数据量大

第二点:要具备唯一性(数据量过大时分表,可能出现重复id的问题)

因此要使用==全局唯一ID==在分布式系统下用来生成全局唯一ID的工具

需要满足:

  • 唯一性
  • 高可用
  • 高性能
  • 递增性(有利于索引和插入)Redis的incr
  • 安全性(自增不能规律过于明显)

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

时间信息与 Redis 自增序列组合全局 ID

同一业务与日期命名空间中的应用实例,共用 Redis 计数器获取自增序列,再与约定的时间信息组成 ID。编码需要明确字段边界、日期切换和序列溢出的处理,不能随意拼接便认为绝不会重复。这是 Redis 自增组合方案,与通常包含机器标识的 Snowflake 应区别理解,也不代表 ID 已不可猜测。

全局唯一ID生成策略:.

  • UUID
  • Redis自增snowflake算法
  • 数据库自增
  • Redis自增ID策略:
    • 每天一个key,方便统计订单量.
    • ID构造是时间戳+计数器

下单时需要判断两点:

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

秒杀活动时间校验与条件扣库存流程

活动时间不符合条件时直接拒绝下单;活动进行中也不能仅凭先前读到的库存继续扣减。应在数据库更新时同时约束库存大于零,并依据更新结果决定是否创建订单。库存扣减与订单写入需要事务边界,而同一用户同一优惠券只能购买一次,还需要额外的并发约束。

==高并发情况下会出现超卖==

超卖的风险来自检查与修改之间的并发窗口:多个请求可能先后读到同一个可售库存,再各自继续下单。若扣减没有同步约束,就可能卖出超过可用数量的订单,或者发生更新覆盖。把库存条件放进更新语句,并检查影响行数,才能让过时的读取结果不再直接决定是否售出。

悲观锁把冲突操作放进互斥区,让其他执行者等待;乐观方案则允许先读取,在提交更新时用版本号或条件比较拒绝过期修改。前者需要评估锁粒度与等待成本,后者需要处理冲突失败和重试,不能只用“更快”概括选择。进程内同步锁也不会自动约束另一台应用实例。

乐观锁的实现方式:

  • 版本号法
  • CAS

1.悲观锁:添加同步锁,让线程串行执行

  • 优点:简单粗暴
  • 缺点:性能一般

2.乐观锁:不加锁,在更新时判断是否有其它线程在修改

  • 优点∶性能好
  • 缺点∶存在成功率低的问题

修改秒杀业务,要求同一个优惠券,一个用户只能下一单

p54

集群下的并发安全问题

进程内锁与共享 Redis 锁的作用边界

同一用户的请求分散到不同应用进程后,各进程自己的同步锁无法相互排斥。需要让相同业务资源竞争同一个共享锁键,只有获锁者进入受保护业务,未获锁者等待、重试或返回。锁键粒度应匹配优惠券与用户的业务约束,数据库唯一约束也可作为防止重复订单的最终保护。