令牌桶限流/防超卖

如何保障购票时系统承载高并发而不出问题?

对于五一、国庆以及过年这些节日来说,一些热门列车的 TPS 少说有几十万 ,很有可能产生瞬时高并发压垮系统的问题,因此必须要对购票服务限流。

最开始计划采用余票缓存来展示余票数量并实现限流效果。这种方式下,系统会将列车余票数据缓存到内存中,以便快速响应前端查询请求。同时,通过缓存中的余票数量来控制购票请求的并发量,避免系统过载。

然而,余票缓存做限流也存在一定的风险。特别是在极端情况下,如扣减余票缓存成功后应用宕机,会导致余票缓存和余票数据库库存不一致。这种情况下,前端查询缓存可能会展示余票为0,但数据库实际仍有余票。这种时候,类似于电商的处理,余票为 0 的记录是不允许用户再提交订单的。为了避免用户因余票数量不准确而无法购票,系统需要采取额外的措施来保障购票流程的顺畅。

为了解决这个问题,系统引入了令牌桶限流机制。

令牌桶限流是一种常用的限流算法,它通过一个令牌容器来存放令牌,每个令牌代表一张列车余票。用户购票时需要先尝试从令牌容器中扣减令牌。如果扣减成功,表示余票数量充足,用户可以继续进行后续的座位分配和订单创建流程;如果扣减失败,则请求被拒绝,避免了对后端数据库的过度压力。

为了进一步提高系统的稳定性和可靠性,系统在令牌桶限流的基础上还增加了二次检查机制。当发现没有剩余可用令牌时,系统会触发一个请求去比对数据库是否还有余票。如果数据库有余票,那么系统会将令牌容器缓存删除,并在下一个用户购票时重新加载。

如果我们用了令牌遇到上述极端情况,令牌余量为 0,余票缓存余量正常,数据库正常。这个时候,只要余票缓存不为 0,那么哪怕令牌为空,也可以让用户正常下单,进而触发刷新令牌容器的操作。

除此之外,在扣减令牌,分配座位成功后,系统会立即将数据库中的座位状态修改为已锁定,防止其他用户同时购买同一个座位。同时,通过Canal数据库变更监听工具,系统会实时监控到座位状态的变更,并更新余票缓存,确保缓存中的余票数据与数据库保持一致。

令牌桶限流如何实现?

一、令牌桶的初始化和存储

  1. 令牌桶的创建
    • 在Redis中创建一个特定结构的Key,用于存储令牌桶的信息。这个Key由车票服务标识、令牌限流容器的标识以及列车ID信息组成。
  2. 令牌桶的存储
    • 令牌桶采用 Hash结构, Key 由此列车出发站,终点站以及座位类型组成,对应的 Value 就是列车座位余量。
    • 比如id为2的火车对应的令牌桶,有key为北京南_杭州东_0,对应的val为10,代表由北京南驶向杭州东的2号火车的商务座余票量为10,还有key为北京南_南京南_2 ,val为800,代表由北京南驶向南京南的2号火车的二等座余票量为800。
    • 而当购买一张票时,还要扣减中间站点以及沿途各站的余量,比如2号火车的行驶路线由北京南站—南京南站—杭州东站 ,如果用户买了北京南站—杭州东站的火车票,同时也要扣减北京南站—南京南站,南京南站—杭州东站的火车票。

二、令牌获取流程

  1. 令牌容器存在性检查
    • 在尝试获取令牌之前,系统首先会检查令牌容器是否在Redis中有效。如果令牌容器失效,系统会重新读取并放入缓存。
  2. 准备Lua脚本执行数据
    • 然后系统会准备一系列参数,包括存储Redis Hash结构的Key值、用户购买的出发站点和到达站点、需要扣减的相关列车站点以及座位类型和对应数量,这些参数将被传递给Lua脚本。
  3. 执行Lua脚本获取令牌
    • 最后,通过客户端调用 Redis 执行 Lua 脚本。判断当前令牌数是否足够满足用户的请求。如果令牌不足,则直接返回失败,如果足够,扣减出发站点和到达站点以及相关的车站令牌余量
  4. Lua脚本的原子性
    • Lua脚本在Redis中的执行是原子性的,保证了在高并发环境下,令牌桶状态的更新和令牌的获取是安全的。

如何防止库存超卖

为了防止库存超卖,系统采取了以下措施:

  1. 令牌桶限流:
    • 通过令牌桶限流机制,确保只有在获取到令牌的情况下,用户才能继续购票流程。
    • 令牌的数量与列车座位余量一一对应,从而避免了超卖的情况。
  2. 订单锁定机制:
    • 在扣减令牌、分配座位成功后,系统会立即将数据库中的座位状态修改为已锁定。
    • 这一机制防止了其他用户同时购买同一个座位,从而避免了库存超卖的风险。
  3. 缓存与数据库一致性:
    • 通过Canal数据库变更监听工具,系统能够实时监控座位状态的变更,并更新余票缓存。
    • 这确保了缓存中的余票数据与数据库保持一致,避免了因缓存不一致而导致的超卖问题。

通过令牌桶限流机制、订单锁定机制以及缓存与数据库一致性保障等措施,系统能够有效地防止库存超卖问题,确保购票流程在高并发下的顺畅进行。


令牌桶限流/防超卖
https://xichicheng.github.io/2024/11/20/令牌桶限流_防超卖/
作者
陈兮迟
发布于
2024年11月20日
许可协议