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

不使用乐观锁与悲观锁,如何解决酒店预订服务方法的并发问题?

可行解决方案

你当前的代码问题在于应用层的库存校验和预订插入不是原子操作,并发场景下多个请求会同时查询到相同的剩余可订数量,都通过校验后重复插入,导致超售。你不需要新增带version的库存实体,也不需要加全局服务锁,用下面的类乐观锁方案即可实现需求:

方案1:数据库原子条件插入(最适配你的场景)

直接把可用性校验逻辑下沉到数据库层面,和预订插入合并为单个原子操作,利用Oracle的事务原子性保证同一时间只有一个请求能插入成功,完全符合你要的「两个请求都可发起,其中一个校验失败抛异常」的要求。

实现逻辑

用INSERT ... SELECT语法编写原生SQL,只有当对应时段对应房型的可订数量满足需求时才执行插入,通过返回的插入行数判断是否预订成功:

  1. 先调用酒店服务拿到对应房型的总房量(你现有逻辑已经实现)
  2. 编写Repository层的原生插入方法,示例SQL如下:
INSERT INTO booking (id, hotel_id, check_in_date, checkout_date, 其他你需要的字段)
SELECT :bookingId, :hotelId, :checkInDate, :checkoutDate, 其他参数
FROM DUAL
WHERE (
    -- 计算当前房型的可订数量:总房量 - 对应时段已订数量
    :totalRoomsOfType - COALESCE(
        (SELECT SUM(rbd.no_rooms)
         FROM booking b
         JOIN room_booking_details rbd ON b.id = rbd.booking_id
         WHERE b.hotel_id = :hotelId
           AND rbd.hotel_room_id = :roomTypeId
           AND b.check_in_date < :checkoutDate
           AND b.checkout_date > :checkInDate),
    0)
) >= :requiredRoomCount
  1. 服务层调用上述插入方法后判断返回的影响行数:如果行数为1则预订成功,行数为0直接抛出OptimisticLockException即可。

优势

  • 不需要修改现有表结构,不需要新增带version的实体
  • 无应用层锁,并发性能高
  • 数据库自动保证操作原子性,不会出现超售

可选方案:分布式锁(多实例部署时的兜底方案)

如果你觉得SQL太复杂不好维护,可以引入Redis分布式锁,锁的粒度为hotelId:roomTypeId:checkInDate:checkoutDate,校验和插入逻辑执行前先抢锁,抢不到直接抛异常。不过这个方案属于悲观锁范畴,并发性能略低于方案1。

注意事项

  • Oracle默认的READ COMMITTED事务隔离级别完全满足需求,不需要调整
  • 调用酒店服务拿总房量的逻辑尽量和插入操作紧挨,避免总房量变更导致校验不准

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 04:30:03