像 Booking.com 或 Airbnb 这样的酒店预订系统是一个常见的系统设计面试问题。

它看起来简单,直到它以最糟糕的方式崩溃。

两个用户在同一秒点击最后一个可用房间的”预订”。你的 API 做 SELECT 检查,所以两个请求都看到可用性,你为一张房间收取两张信用卡。接下来,你处理退款、愤怒的客户,以及一个无法信任的系统。

那不是 bug……那是如果你不正确设计它的默认结果。

这就是为什么 Airbnb 和 Booking.com 不仅仅是”表 + 端点”。

大多数开发者直接跳到盒子和箭头而不理解实际问题。他们错过并发陷阱,忘记双重预订,无法解释权衡。

在这期通讯中,我们将以面试和真实流量要求的方式设计它:库存的强一致性、搜索的快速读取,以及缓存和扩展的清晰权衡。

在我们设计任何东西之前,让我们放大,定义真正的问题,并逐步列出方法。

继续前进!

设计需求 - 如何范围系统

每个架构良好的系统设计从约束开始。

如果你跳过这一步,之后的所有都是猜测。

以下是我们将问的一些澄清问题关于设计需求。

规模

  • 多少酒店?
  • 总共多少房间?
  • 单一连锁还是像 Booking.com 这样的平台?

支付时间

  • 预订时支付还是入住时支付?

预订渠道

  • 仅 Web?
  • 移动应用?
  • 电话预订?

取消

  • 用户可以取消吗?
  • 政策是什么?

超额预订

  • 我们支持它吗?(酒店通常允许 5-10% 超额预订)

动态定价

  • 房间价格根据需求/日期变化吗?

澄清这些约束后,我们现在可以定义系统实际必须做什么。

功能需求

  • 酒店管理:管理员可以添加、更新和删除酒店和房间类型。他们调整价格和库存。
  • 搜索和发现:用户可以按位置、日期、客人数量、价格范围和设施搜索酒店。
  • 预订流程:用户可以预订房间、查看预订和取消预订。系统应防止双重预订。
  • 支付处理:与支付网关集成。并优雅地处理故障。
  • 通知:发送预订、取消和支付的确认。

非功能需求

  • 强一致性:两个用户不能为相同日期预订同一房间。不可协商。
  • 高并发:处理数千用户同时尝试预订同一酒店。
  • 低延迟:搜索结果低于 500ms。预订确认在 2-3 秒内。
  • 高可用性:99.9%+ 正常运行时间。
  • 可扩展性:水平扩展而无需重新设计系统。

粗略估计计算

Booking Holdings 报告 2024 年超过 11 亿房间夜。

让我们将其转化为可用数字:

房间夜每年:11 亿

平均住宿:每次预订 2 晚

每年天数:365

每年预订数:

11 亿房间夜 / 每次预订 2 晚 ≈ 5.5 亿预订/年

每天预订数:

5.5 亿 / 365 ≈ 150 万预订/天

每秒预订数(TPS):

150 万 / 86,400 ≈ 17 预订/秒

在 Booking.com 规模:

  • 每天约 150 万预订
  • 平均每秒约 17 预订

Similarweb 显示 booking.com 每月约 5.165 亿访问,每次访问约 7.56 页。

每天访问:

5.165 亿 / 30 ≈ 1700 万访问/天

每秒访问:

1700 万 / 86,400 ≈ 200 访问/秒

如果我们假设每次访问约 5 次后端读取(搜索查询、可用性检查、定价):

后端读取 QPS:

200 访问/秒 × 5 ≈ 1000 读取请求/秒

关键洞察

  • 这是强读重的
  • 你处理约 1000 读取 QPS 和仅约 17 写入 TPS
  • 你的架构应优化读取同时保持写入一致

库存表容量规划

对于库存表,按房间类型和日期跟踪可用性。

假设:

  • 平均酒店入住率:70%
  • 每年售出房间夜:11 亿

年度总容量:

11 亿 / 0.7 ≈ 15.7 亿房间夜/年

平台上总房间数:

15.7 亿 / 365 ≈ 430 万房间

如果平均酒店有 50 个房间:

  • 全球约 85,000 家酒店

房间类型库存表行数:

  • 酒店:约 85,000
  • 每家酒店平均房间类型:10
  • 范围:2 年 = 730 天

行数:

85,000 × 10 × 730 ≈ 6.2 亿行

假设每行约 100 字节:

存储 ≈ 6.2 亿 × 100 字节 ≈ 62 GB 原始数据

现实检查

带索引(通常是表大小的 2-3 倍)、相关表(预订、酒店、客人),你看起来像 500GB+ 总存储。

所以你需要适当的”分片和索引策略”。

理解规模和数据大小后,我们可以设计一个实际适合这些数字的架构……

高层设计

我们使用服务导向架构与清晰边界,但保持”紧密耦合”数据在同一数据库。

Airbnb 架构

客户端层

  • Web 和移动应用
  • CDN 用于静态资产(图片、JavaScript、酒店照片)

API 网关

  • 单一入口点
  • 认证、速率限制、请求路由
  • 在服务实例之间负载均衡

服务

  • 酒店服务:酒店和房间信息。 mostly static,重缓存
  • 费率服务:按日期的房间定价。基于需求的动态定价
  • 预订服务:处理预订逻辑、库存检查和预订创建/取消。这是并发重要的地方
  • 支付服务:与支付网关集成。更新预订状态
  • 通知服务:发送电子邮件和推送通知
  • 搜索服务:管理 Elasticsearch 集成用于酒店发现

数据层

  • PostgreSQL:主事务存储。ACID 保证
  • Redis:热数据缓存
  • Elasticsearch:搜索功能(全文、地理空间、过滤)

架构决策

在这个规模上,纯单体或完整微服务设置将是次优的。

单一单体简化事务但难以独立扩展和部署。带独立数据库的完整微服务引入分布式事务和一致性问题,在这个流量水平不存在。

这个设计使用服务级分离与共享事务数据用于紧密耦合域(预订 + 库存)。它在应用层保持部署灵活性,同时在最重要的地方保持强一致性。

每个服务拥有其域并独立扩展。

酒店服务可能需要 10 个实例用于搜索流量,而预订服务只需要 3 个因为预订 TPS 低。

关键决策

预订和库存数据生活在同一数据库。

这让我们使用本地 ACID 事务而不是分布式事务。我们在应用层有服务级分离,但对紧密耦合实体共享数据。

这是务实的。

分布式事务为这里最小利益添加巨大复杂性。替代方案(带 saga 模式或 2PC 的独立数据库)是过度杀伤,当你可以将相关数据保持在一起时。

替代方案

将预订和库存分成独立数据库,并用 Saga 模式或两阶段提交(2PC)协调。

2PC 提供强一致性但增加延迟、阻塞和故障复杂性。协调器故障可能停滞系统。

Saga 模式避免锁定但引入最终一致性、补偿动作和复杂故障处理。这对用户面对预订流程有风险,其中”最终一致”意味着愤怒用户。

在约 17 写入 TPS,增加的复杂性不合理……本地 ACID 事务更简单、更安全、更快。

API 设计

所有操作的 RESTful API:

酒店管理

  • GET /v1/hotels/{id} - 获取酒店详情
  • POST /v1/hotels - 添加新酒店(仅管理员)
  • PUT /v1/hotels/{id} - 更新酒店信息(仅管理员)
  • DELETE /v1/hotels/{id} - 删除酒店(仅管理员)

房间管理

  • GET /v1/hotels/{id}/rooms/{id} - 获取房间类型详情
  • POST /v1/hotels/{id}/rooms - 添加房间类型(仅管理员)
  • PUT /v1/hotels/{id}/rooms/{id} - 更新房间类型(仅管理员)
  • DELETE /v1/hotels/{id}/rooms/{id} - 删除房间类型(仅管理员)

预订管理

  • GET /v1/reservations - 获取用户的预订历史
  • GET /v1/reservations/{id} - 获取特定预订详情
  • POST /v1/reservations - 创建新预订
  • DELETE /v1/reservations/{id} - 取消预订

搜索

  • GET /v1/search
  • 参数:location、checkIn、checkOut、guests、beds、priceRange
  • 返回:可用酒店和房间类型

幂等性键

POST /v1/reservations 请求包括幂等性键:

{
"idempotency_key": "reservation-uuid"
}

这个 reservationId 在前端生成并防止用户多次点击提交时双重预订。

处理并发

以下是关键洞察:

问题

双重预订发生在两个用户同时尝试预订同一房间时。

解决方案

  1. 数据库锁

    • 悲观锁:SELECT FOR UPDATE
    • 乐观锁:版本号检查
  2. 原子操作

    UPDATE inventory
    SET available = available - 1
    WHERE room_id = ? AND date = ? AND available > 0
  3. 队列系统

    • 串行化预订请求
    • 确保顺序处理
  4. 分布式锁

    • Redis 锁
    • Zookeeper 锁

本文为学习目的的个人翻译,译文仅供参考。

原文链接:Airbnb System Design

版权归原作者或原刊登方所有。本文为非官方译本;如有不妥,请联系删除。