You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

酒店预订系统5分钟房间保留功能实现方案及Redis锁疑问咨询

酒店房间5分钟锁定的系统设计方案探讨

问题背景

面试中遇到的系统设计问题:如何为用户保留房间5分钟?

库存数据结构

Hotel-idroom_type_iddatetotal_inventorytotal_reserved
1230012023-09-3010099
1230012023-10-0110096

业务流程

用户进入预订详情页后,Reservation Service先从缓存查询库存,随后需要锁定机制为用户保留房间5分钟。以2023-09-30为例,剩余1间可用房:

  • 用户A操作时,可继续完成预订;
  • 若此时用户B尝试操作,系统需识别该房型当日已被锁定,提示“该房间不可用”。

我的疑问

  • 是否应采用Redis锁管理实现该功能,还是有其他方案?
  • 锁定机制具体如何运作?实际锁定的是什么?理想情况下不应锁定整行数据,因为当total_inventory - total_reserved > 0时,其他用户仍应可预订房间,对吗?

我的初步猜想

曾提出用Redis锁锁定房间并设置5分钟TTL自动释放,但无法清晰解释步骤,具体猜想如下:

  1. 在Redis中为每个库存条目添加on-hold字段,示例:
key = 123_001_2023_09_30  # 格式:hotel_id_room_type_id_date
value = { total_inventory: 100, total_reserved: 99, on_hold: 1}
TTL = 5分钟
  1. 其他用户尝试预订时,先检查total_reserved + on_hold >= total_inventory,若为真则阻止操作;
  2. 5分钟到期后,Redis自动将on_hold值减1(不确定是否可行)

专业解答

1. 技术方案选择:Redis是最优解,也可搭配数据库方案

  • Redis方案:适合高并发场景,能快速响应库存查询与锁定操作,天然支持TTL自动释放,是这类临时锁定需求的首选。
  • 备选方案:
    • 数据库乐观锁:通过版本号或CAS(Compare And Swap)操作控制,但高并发下冲突率高,性能不如Redis;
    • 数据库悲观锁:直接锁定整行数据,会导致并发能力骤降,不符合“剩余库存>0时其他用户仍可预订”的需求,不推荐。

2. 锁定机制的核心逻辑:锁定单份可预订库存额度,而非整行数据

你的理解是对的——不能锁定整行数据,否则会浪费剩余库存的预订能力。实际锁定的是当前用户申请的那1间(或N间)可预订房间额度,核心流程如下:

完整Redis实现步骤

  1. 初始化缓存库存:将数据库中的库存数据同步到Redis,结构采用哈希表(Hash)存储,每个hotel_id_room_type_id_date作为哈希键,字段包括total_inventory、total_reserved、on_hold(初始为0)。
  2. 用户申请锁定:
    • 用Redis Lua脚本执行原子操作(避免并发竞争):
      -- 计算可用库存 = total_inventory - total_reserved - on_hold
      local available = tonumber(redis.call('HGET', KEYS[1], 'total_inventory')) - tonumber(redis.call('HGET', KEYS[1], 'total_reserved')) - tonumber(redis.call('HGET', KEYS[1], 'on_hold'))
      if available >= 1 then
          -- 可用库存足够,增加on_hold计数
          redis.call('HINCRBY', KEYS[1], 'on_hold', 1)
          -- 设置键的TTL为5分钟(若键已存在则刷新TTL)
          redis.call('EXPIRE', KEYS[1], 300)
          return 1  -- 锁定成功
      else
          return 0  -- 锁定失败
      end
      
    • 若脚本返回1,说明锁定成功,允许用户进入后续预订流程;返回0则提示“房间不可用”。
  3. 锁定到期或预订完成后的清理:
    • 预订完成:用户成功支付后,调用Lua脚本原子更新total_reserved +=1,同时on_hold -=1;
    • 锁定到期:Redis的TTL到期后无法自动修改on_hold值,需通过两种方式处理:
      • 开启Redis键空间通知,监听键过期事件,触发服务端逻辑将对应on_hold减1;
      • 定时任务扫描Redis中所有库存键,清理过期的锁定额度(适合低并发场景)。

3. 对你的猜想的点评

你的思路方向是对的,但有一处关键问题:Redis的TTL到期后无法自动执行on_hold减1的操作,因为TTL仅负责删除键,没有内置的字段修改逻辑,需要通过上述的键空间通知或定时任务来实现锁定额度的回收。

另外,建议用Redis哈希表存储每个字段,而非序列化的对象,这样能更高效地单独修改on_hold、total_reserved等字段,避免序列化/反序列化的开销。

内容的提问来源于stack exchange,提问作者zzf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 03:53:17