使用MySQL实现类Airbnb应用的房源可用性更新逻辑
类Airbnb短租房源MySQL可用性落地方案
核心表结构设计
不要在房源主表存动态可用状态,主表只存房源静态信息即可,可用性判定核心靠两张表:
listings房源基础表:存房源ID、房东信息、房屋配置、入住/退房规则等静态字段,加主键索引即可bookings预订核心表(可用性判定的唯一真值源),必须字段如下:booking_id主键listing_id关联房源ID,必须加普通索引check_in_date入住日期,DATE类型check_out_date退房日期,DATE类型status预订状态,用ENUM类型存枚举值:confirmed(已确认生效)、cancelled(已取消)、completed(已完成)、pending(待支付/待确认)- 其余关联用户ID、订单金额、创建时间等字段按业务需求加即可
- 可选性能优化表
listing_calendar:如果需要做房源日历页快速渲染,可以加这张冗余表,字段为listing_id、calendar_date、is_available、blocked_reason、booking_id,所有数据变更以bookings表为准。
预订生效时标记时段不可用的逻辑
不要尝试给房源打全局不可用标记,短租的不可用是时段性的,所有占用逻辑围绕有效预订记录实现,操作全流程放数据库事务里执行,避免并发问题:
- 先对目标房源加行写锁,防止并发超售:
SELECT 1 FROM listings WHERE listing_id = ? FOR UPDATE; - 做预订时段冲突校验,判断用户选中的入住/退房时段是否和已生效订单重叠,SQL写法如下:
如果返回SELECT COUNT(*) AS conflict_count FROM bookings WHERE listing_id = ? AND status = 'confirmed' -- 日期重叠判断逻辑:已存在订单的入住日早于用户选的退房日,且已存在订单的退房日晚于用户选的入住日即为冲突 AND check_in_date < ? -- 传入用户选择的check_out_date AND check_out_date > ?; -- 传入用户选择的check_in_dateconflict_count大于0,直接返回时段冲突,终止预订流程;计数为0则说明时段可订。 - 写入预订记录完成占用:直接往
bookings表插入一条status='confirmed'的对应记录即可,不需要额外修改其他表的状态——后续所有可用性查询,都只统计status='confirmed'的订单作为不可用时段依据。 - 如果用了
listing_calendar冗余表,就在同一事务里,把预订覆盖的日期段(入住日到退房日前一天,比如1号入住3号退房,占用1、2号两晚)的is_available更新为0,blocked_reason设为'booked',关联对应booking_id。
取消预订恢复可用的逻辑
同样在数据库事务里执行:
- 找到对应要取消的预订记录,直接把
status字段更新为cancelled即可。因为之前的冲突校验逻辑只统计confirmed状态的订单,取消的订单不会被计入占用,对应时段自然恢复可订状态,不需要做复杂的时段拆分合并。 - 如果用了
listing_calendar冗余表,同一事务里把关联该booking_id的日历记录的is_available更新为1即可;如果冗余表没存booking_id关联,就先查下对应时段有没有其他confirmed状态的订单覆盖,没有的话再改回可用,避免把其他订单占用的日期错误放开。
避坑说明
不要用以下几种常见的错误实现:
- 不要在房源表存is_available全局标记,完全无法处理时段性占用的需求
- 不要把不可用时段存在JSON字段里,MySQL无法对JSON内的日期做高效范围查询,并发场景下超售概率极高
- 不要单独维护一张不可用时段表做拆分合并,遇到改期、部分取消的场景逻辑会异常复杂,以订单表为真值源的方案逻辑最简洁,出问题排查也简单。
内容的提问来源于stack exchange,提问作者Fabian Sarango Guayllas
相关产品推荐
相关产品推荐

