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

设计一个支持百万级用户同时预约在线课程(如好未来在线的直播课)的系统,需要考虑高并发、数据一致性(如课程容量限制)、实时通知(预约成功/失败)。请描述系统的整体架构,关键模块的设计,以及如何处理并发控制(如锁、无锁数据结构)和数据一致性(如分布式锁、最终一致性)。

好未来前端 - C++难度:困难

答案

1) 【一句话结论】
针对百万级用户在线课程预约,系统采用微服务架构,通过请求分片分散压力,结合Redis分布式锁保障库存一致性,利用消息队列异步通知,结合缓存+数据库事务实现最终一致性,平衡高并发与数据一致性。

2) 【原理/概念讲解】
高并发预约场景的核心挑战是用户请求量(百万级)、课程容量限制(防超卖)、实时通知。系统分四层设计:前端API网关(请求分片)、后端预约服务(扣减库存、事务处理)、中间件(分布式锁、消息队列)、数据库(持久化数据)。关键设计点:

  • 请求分片:按课程ID或时间维度将用户请求路由到不同后端实例,避免单点过载。
  • 分布式锁:用Redis SETNX实现跨实例互斥,扣减库存前加锁,防止超卖;锁过期时间设为库存扣减时间+缓冲(如10秒),避免死锁。
  • 异步处理:预约成功后,通过Kafka发布消息,用户服务消费后推送通知,避免阻塞主流程。
  • 缓存与数据库一致性:课程容量存入Redis(读缓存),减少数据库压力;更新时通过消息队列同步,保证最终一致性(用户可见性)。
  • 数据库事务:预约流程(扣减容量、插入记录)用事务(隔离级别REPEATABLE READ),防止脏读或不可重复读,同时考虑性能,避免过度锁竞争。

3) 【对比与适用场景】

对比项分布式锁(Redis SETNX)数据库行级锁
定义跨实例互斥,通过Redis SETNX命令,第一个成功设置键的实例获得锁。数据库自身事务控制,行级锁保护数据操作。
特性依赖外部存储,实现简单;锁过期时间可配置;支持高可用。依赖数据库,性能受数据库影响;锁竞争高时可能阻塞。
使用场景跨服务、跨实例的互斥操作(如课程容量扣减),需要高可用。单服务内的事务控制,或数据库自身的事务隔离。
注意点锁过期时间设置不当可能导致死锁或超卖;锁竞争高时性能下降。事务隔离级别选择影响性能,行级锁可能导致锁等待。

4) 【示例】(伪代码,用户预约流程)
用户发送请求(课程ID: 101,用户ID: 1001)到API网关。

  1. API网关按课程ID分片,路由到后端实例1。
  2. 后端检查Redis缓存课程容量(key: course:capacity:101),若剩余>0:
    • 尝试获取Redis锁(key: course:lock:101,value: 1001,过期时间=库存扣减时间+缓冲,如10秒)。
    • 锁成功后:
      • 数据库执行事务(隔离级别REPEATABLE READ):UPDATE course SET capacity = capacity - 1 WHERE id = 101; INSERT INTO booking (course_id, user_id, create_time) VALUES (101, 1001, NOW())。
      • 发布消息到Kafka(topic: booking.success,消息体包含用户ID和课程ID)。
      • 解锁Redis(DEL course:lock:101)。
    • 返回成功响应(200,“预约成功”)。
  3. 若缓存容量为0:直接返回失败(400,“课程已满”)。

5) 【面试口播版答案】
“面试官您好,针对百万级用户预约在线课程,我设计的系统核心是采用微服务架构,通过请求分片、分布式锁、异步消息、缓存+数据库事务等手段,实现高并发下的数据一致性。用户请求由API网关按课程ID分片,路由到不同后端实例,避免单点压力。关键模块包括:1. 分布式锁:用Redis的SETNX实现,扣减课程容量前加锁,防止超卖,锁过期时间设为10秒(库存扣减时间+缓冲),避免死锁;2. 异步处理:预约成功后,通过Kafka消息队列通知用户,提升响应速度;3. 缓存与数据库一致性:课程容量存入Redis缓存(读缓存),减少数据库压力,更新时通过消息队列同步,保证最终一致性;4. 数据库事务:预约流程用REPEATABLE READ隔离级别的事务,确保数据一致性。整体架构通过这些技术,平衡了性能与一致性,支持百万级并发。”

6) 【追问清单】

  • 问题1:分布式锁的可靠性如何保障?比如锁过期后,另一个实例会误判为可操作?
    回答要点:设置合理锁过期时间(如10秒),并实现重试机制(指数退避,重试3次后失败),结合请求分片减少锁竞争。
  • 问题2:如何处理缓存雪崩?比如大量用户同时访问缓存,导致缓存失效?
    回答要点:使用缓存预热(提前加载热门课程数据),或设置缓存过期时间随机偏移(Jitter算法),避免集中失效;若缓存穿透,用布隆过滤器过滤无效请求。
  • 问题3:最终一致性下的用户体验如何保障?比如消息队列延迟导致库存未扣减?
    回答要点:通过定时任务检查未确认的预约记录(如每分钟检查),重新处理未确认的预约,确保库存最终正确。
  • 问题4:数据库事务的隔离级别选择依据?比如是否需要SERIALIZABLE?
    回答要点:根据业务需求,预约场景对一致性要求高,REPEATABLE READ足够,能防止脏读和不可重复读,同时减少锁竞争,避免过度性能损耗。
  • 问题5:系统如何监控课程容量接近阈值?比如剩余10%时告警?
    回答要点:通过Prometheus收集课程容量指标,设置告警阈值(如剩余容量<10%),及时扩容或通知运维。

7) 【常见坑/雷区】

  • 锁过期时间设置不当:若过期时间太短导致死锁,太长则资源占用。需根据业务场景(如课程预约时长)调整,比如课程预约有效期为1小时,锁过期时间设为1.2倍。
  • 缓存穿透:若缓存未命中且数据库查询失败(如课程不存在),可能导致大量请求直接访问数据库。需实现缓存空值策略(如设置默认值或布隆过滤器,过滤无效请求)。
  • 最终一致性带来的问题:用户可能看到预约成功但实际未扣减库存(如消息队列延迟),需通过重试或补偿机制(如定时任务检查未确认的预约记录,重新处理)。
  • 分布式锁的竞争:若多个实例同时请求同一课程,锁竞争高导致性能下降,可通过请求分片减少竞争,或使用更高效的锁实现(如Redis Redlock算法)。
  • 数据库事务隔离级别:若使用SERIALIZABLE,可能导致锁竞争严重,影响性能,需根据业务需求权衡,比如对于非关键业务,REPEATABLE READ即可满足一致性要求。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1