51mee - AI智能招聘平台Logo
模拟面试题目大全招聘中心会员专区

设计一个高并发的旅游酒店预订系统,需支持大促期间(如春节)的百万级订单处理,请描述系统架构,包括前端、后端、数据库、缓存、消息队列等组件,以及如何保证订单的实时性和一致性。

南光(集团)有限公司旅游酒店类难度:困难

答案

1) 【一句话结论】采用微服务架构+分布式事务(Saga模式)+多级缓存+异步消息队列+数据库分库分表(ShardingSphere实现),通过幂等性、缓存预热、消息可靠性及补偿幂等性,保障百万级订单大促下的实时性与一致性。

2) 【原理/概念讲解】系统整体架构分层,各组件功能与设计要点:

  • 前端层:Nginx负载均衡处理请求,静态资源缓存(CDN加速首屏加载)。
  • 后端层:微服务拆分(用户、订单、库存、支付等),独立部署,降低耦合。
  • 数据库层:分库分表+读写分离+索引优化:
    • 分库:按酒店ID范围分库(如酒店ID 1-1000→库1,1001-2000→库2),每库对应固定酒店数,大促前根据历史数据(如去年大促并发量)估算库数量(如10库),动态扩容(ShardingSphere支持按需增加库节点)。
    • 分表:订单表按时间分表(order_2024_01_01等),库存表按酒店ID+时间分表(stock_hotel_1001_2024_01),减少单表数据量,提升读写性能。
    • 索引优化:订单表按order_id(唯一索引)和status(如created)索引;库存表按hotel_id+quantity(主键+索引)索引,确保分库分表后查询/写入高效。
  • 缓存层:本地缓存(Java本地缓存,存储用户信息、常用酒店列表,读取速度极快)+分布式缓存(Redis集群,存储实时库存、订单状态):
    • 本地缓存:热点数据,如用户登录态,避免网络延迟。
    • 分布式缓存:高并发热点数据,如库存,需分布式锁(Redis锁)保证并发安全,缓存失效时通过布隆过滤器过滤无效请求(缓存穿透)。
  • 消息队列层:异步处理非核心流程(短信、支付回调、日志),采用Kafka(分区数=并发量/1000,如百万级并发→100分区;副本因子=3,持久化消息;事务性消息确保发送成功,失败重试,死信队列处理积压消息)。
  • 分布式事务:Saga模式(每个服务独立事务,通过消息队列协调状态,最终补偿失败服务):
    • 订单创建流程:扣减库存(本地缓存+数据库事务)→生成订单(订单表事务)→发送消息到Kafka→库存/支付/短信服务消费消息处理。
    • 补偿机制:库存服务失败时,订单服务触发补偿,重新扣减库存(检查库存是否已恢复,避免重复补偿)。
  • 一致性保证:订单号幂等性(检查状态后处理,避免重复下单);缓存预热(大促前定时任务加载热门酒店库存到缓存,减少缓存失效压力);消息队列可靠性(Kafka持久化+事务性消息+死信队列);Saga补偿幂等性(状态检查,如库存是否已恢复,避免重复补偿)。

3) 【对比与适用场景】:

  • 数据库分库分表 vs 读写分离:
    对比项分库分表(ShardingSphere)读写分离(主从复制)
    定义按业务维度(如酒店ID)拆分数据库节点,分表主库写,从库读,数据同步
    特性扩展性(按需增加节点)读性能提升,写性能依赖主库
    使用场景高并发写入/读取(如百万级订单)读多写少场景(如查询订单)
    注意点分片规则设计(如酒店ID范围)从库延迟(同步延迟)
  • 缓存穿透 vs 缓存雪崩:
    对比项缓存穿透(无效请求)缓存雪崩(大量缓存失效)
    定义无效key(如不存在的酒店ID)直接访问数据库大量key同时失效,请求直接到数据库
    特性单个请求导致数据库压力大量请求导致数据库雪崩
    解决方案布隆过滤器(过滤无效请求)+ 互斥锁(缓存未命中时加锁,防止缓存击穿)随机过期时间(如3600秒+随机偏移)+ 热点数据预加载(大促前加载热门数据到缓存)
    使用场景防止无效请求冲击数据库缓解大促时缓存失效压力

4) 【示例】(订单创建流程伪代码):

// 前端请求:POST /order/create?order_id=123456&hotel_id=1001
// 订单服务逻辑(幂等性+缓存+数据库+消息)
1. 检查订单号是否已存在(Redis分布式锁,key: "order_exists:123456"):
   - 若已存在且状态为"created",返回成功。
2. 若缓存未命中(或库存<=0):
   a. 查询分布式缓存(Redis)库存(key: "hotel_stock:1001"):
      - 若库存>0:执行数据库事务(主库):
         i. 扣减库存(UPDATE stock SET quantity=quantity-1 WHERE hotel_id=1001 AND quantity>0);
         ii. 更新缓存(SET hotel_stock:1001 (new_quantity) EX 3600 + random(0, 3600));
         iii. 插入订单(INSERT INTO orders (order_id, hotel_id, user_id, amount, status) VALUES (123456, 1001, 1, 299, 'created'));
         iv. 更新用户余额(UPDATE user SET balance=balance-299 WHERE user_id=1);
         v. 发送Kafka消息(主题: "order.created",内容: {"order_id":123456, "hotel_id":1001, "user_id":1, "amount":299})。
      - 若库存<=0:查询数据库库存(SELECT quantity FROM stock WHERE hotel_id=1001)→更新缓存(预热)→同步骤2.a处理。
3. 库存服务消费Kafka消息,更新本地缓存(若需)。
4. 支付服务消费Kafka消息,调用支付接口(异步)。
5. 短信服务消费Kafka消息,发送订单确认短信(异步)。

5) 【面试口播版答案】
面试官您好,针对南光集团旅游酒店预订系统的大促百万级订单需求,我设计的系统核心是采用微服务架构+分布式事务(Saga模式)+多级缓存+异步消息处理。前端通过Nginx负载均衡处理请求,后端拆分为用户、订单、库存、支付等微服务。数据库采用分库分表(ShardingSphere实现)+读写分离,缓存分为本地缓存(提升热点数据读取速度)和分布式缓存(如Redis集群,保证高并发下的数据一致性)。订单创建时,通过订单号幂等性(检查状态后处理)避免重复下单;通过缓存预热(大促前通过定时任务加载热门酒店库存到缓存)缓解缓存雪崩;通过消息队列(Kafka)异步处理非核心流程(如支付、短信),解耦服务。分布式事务采用Saga模式,订单服务先扣减库存(本地缓存+数据库事务),再生成订单(订单表事务),最后通过消息队列通知库存、支付、短信服务,确保强一致性。这样既保证了订单的实时性(秒级响应),又通过缓存和异步处理缓解了高并发压力,确保大促期间百万级订单的一致性。

6) 【追问清单】:

  • 问题1:分布式事务具体如何实现?
    回答要点:采用Saga模式,每个服务独立事务,通过消息队列协调状态,最终补偿失败的服务(如库存服务失败时,订单服务触发补偿,重新扣减库存)。
  • 问题2:如何解决缓存雪崩/穿透问题?
    回答要点:缓存雪崩用随机过期时间(如3600秒+随机偏移)+热点数据预加载(大促前通过定时任务加载热门酒店库存到缓存);缓存穿透用布隆过滤器(过滤无效请求)+互斥锁(缓存未命中时加锁,防止缓存击穿)。
  • 问题3:消息队列如何保证可靠性?
    回答要点:消息持久化(Kafka将消息写入磁盘日志)、事务性消息(确保发送成功,失败重试)、死信队列(处理无法投递的消息,后续人工处理)。

7) 【常见坑/雷区】:

  • 忽略订单幂等性,导致大促时重复下单,影响订单一致性。
  • 数据库分库分表策略不合理(如按酒店ID分库时未考虑大促并发量,导致单库压力过大)。
  • 缓存未做雪崩/穿透处理,缓存失效时大量请求直接访问数据库,引发雪崩。
  • 消息队列未保证可靠性,导致消息丢失,订单状态不一致(如支付回调消息丢失,导致订单状态未更新)。
  • 分布式事务补偿流程缺失,失败服务无法自动恢复,导致订单状态异常。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1