
1) 【一句话结论】通过“分阶段选课流程(预选-确认-补退选)+ Redis分布式锁防重复提交 + 消息队列持久化保障数据 + 缓存预热+布隆过滤器防雪崩”的分层架构,应对开学季高并发,确保数据一致性与用户体验。
2) 【原理/概念讲解】首先解释高并发下的核心矛盾:用户请求量远超系统处理能力,导致响应延迟或系统崩溃。解决思路是“削峰填谷”——通过技术手段(限流、缓存、异步)分散压力,通过流程设计(分阶段)降低单次峰值。
userA_courseC,若不存在则设置锁并写入缓存,否则拒绝。3) 【对比与适用场景】
| 方案类型 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| Redis SETNX防重复 | 为用户-课程组合设置唯一锁 | 防止重复提交,保证单次请求唯一 | 选课预选阶段 | 锁过期时间需合理(避免死锁) |
| 消息队列持久化 | Kafka等持久化存储预选记录 | 确保数据不丢失,支持异步处理 | 选课确认阶段(消费者处理) | 需配置持久化存储(如Kafka的log.dir) |
| 缓存预热 | 提前加载热点数据到Redis | 减少数据库压力,提升响应速度 | 选课系统(课程列表、用户状态) | 需提前计算热点数据范围 |
| 布隆过滤器 | 拦截无效请求 | 避免缓存穿透,减少数据库查询 | 选课系统(课程查询) | 可能存在误判(假阳性) |
4) 【示例】(流程与伪代码):
def pre_select_course(user_id, course_id):
# 限流检查
if not token_bucket.check():
return {"code": 429, "msg": "请求频繁"}
# 分布式锁检查(Redis SETNX)
lock_key = f"lock_user_{user_id}_course_{course_id}"
if not redis_client.setnx(lock_key, 1, ex=60): # 60秒锁过期
return {"code": 409, "msg": "重复提交"}
# 检查用户状态(缓存优先)
user_status = cache.get(f"user_{user_id}")
if not user_status:
user_status = db.query_user(user_id)
cache.set(f"user_{user_id}", user_status, ex=300) # 5分钟缓存
if not user_status["can_select"]:
redis_client.delete(lock_key) # 释放锁
return {"code": 403, "msg": "资格不足"}
# 写入预选缓存
pre_key = f"pre_{user_id}_{course_id}"
cache.set(pre_key, {"status": "pending"}, ex=300)
# 发送消息到Kafka
kafka_producer.send("pre_select_queue", value={"user_id": user_id, "course_id": course_id})
redis_client.delete(lock_key) # 释放锁
return {"code": 200, "msg": "预选成功"}
5) 【面试口播版答案】
“面试官您好,针对开学季选课高并发,我的方案核心是‘分阶段流程+技术防抖’。首先,流程分三阶段:预选(9月1-5日)快速提交,用Redis SETNX防重复;确认(9月6-10日)异步处理,通过事务写入数据库;补退选(9月11-15日)灵活调整。技术上,前端用令牌桶限流,应用层用Redis缓存热点数据,服务层用Kafka持久化预选记录,数据库读写分离。预选阶段,用户提交后,系统先检查Redis锁,若成功则写入缓存+消息队列,返回成功;确认阶段,消费者从Kafka取记录,事务写入数据库,确保数据一致。这样既分散压力,又避免重复提交,保障用户体验和数据安全。”(约90秒)
6) 【追问清单】
7) 【常见坑/雷区】