# 08.面试八股-场景题
# 🔥 场景一:高并发下超卖怎么解决
# 📋 问题描述
面试官:秒杀活动中,10 个人抢 1 个商品,最后卖了 3 个出去,超卖了。你怎么解决?
# 🟢 第一问
问:超卖产生的根本原因是什么?
根本原因:多个线程/请求同时读到库存 > 0,然后各自扣减,导致最终扣减次数超过实际库存。
# 🟡 第二问(追问)
问:怎么解决?说 3 种方案,并比较优劣。
| 方案 | 实现 | 优点 | 缺点 |
|---|---|---|---|
| 数据库乐观锁 | UPDATE SET stock = stock - 1 WHERE id = 1 AND stock > 0 | 简单,不用额外组件 | 高并发下冲突多,性能差 |
| Redis 原子操作 | Lua 脚本扣减 | 性能高 | 数据最终要同步到 DB,可能丢 |
| 分布式锁 | Redisson 锁住扣减过程 | 逻辑清晰 | 性能比 Redis 原子操作差,锁持有时间需要控制 |
-- 防超卖 Lua脚本
local stockKey = KEYS[1]
local deductNum = tonumber(ARGV[1])
-- 获取当前库存
local currentStock = tonumber(redis.call('get', stockKey) or 0)
-- 库存不足
if currentStock < deductNum then
return 0
end
-- 库存充足,原子扣减
redis.call('decrby', stockKey, deductNum)
return 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 🔥 场景二:怎么保证接口安全
# 📋 问题描述
面试官:你写的接口,怎么防止被别人恶意调用?
# 🟢 第一问
问:接口安全的威胁有哪些?说 3 种。
💡 答案要点
| 威胁 | 说明 |
|---|---|
| 未授权访问 | 没登录就能调用需要登录的接口 |
| 越权操作 | 用户 A 删了用户 B 的数据 |
| 恶意刷接口 | 频繁调用导致资源耗尽、费用超标 |
# 🟡 第二问(追问)
问:分别怎么防御?
💡 答案要点
| 威胁 | 防御方案 |
|---|---|
| 未授权访问 | Token/Session 校验 + 白名单接口放行 |
| 越权操作 | 权限校验(Spring Security / Shiro),每次操作检查当前用户是否有权限 |
| 恶意刷接口 | 限流(Sentinel / Redisson RateLimiter)+ 黑名单 |
# 🔥 场景三:线上接口慢怎么排查
# 📋 问题描述
面试官:用户反馈某个接口响应很慢,你怎么排查?
# 🟢 第一问
问:给出完整的排查步骤。
💡 答案要点
从外到内,分层排查:
确认范围:是所有人都慢?还是某个人、某个地区?(客户端 → 网络)
检查监控:看该接口的平均耗时、P99、成功率(是否有监控?没有就加)
看日志:该接口最近的请求日志,有没有异常堆栈?
链路追踪:如果有 SkyWalking/Jaeger,看哪个环节耗时最长(DB?Redis?RPC?)
针对性排查:
DB 慢 → 看慢查询日志,EXPLAIN 分析
Redis 慢 → 检查大 key、慢日志
RPC 慢 → 检查下游服务
压测复现:本地 / 压测环境复现,用 Arthas 等工具定位
# 🟡 第二问(追问)
问:如果定位到是 SQL 慢,你怎么优化?
开启慢日志查询
通过explain分析时间长的sql,看看是不是索引出了问题
然后再检查是否有锁竞争
# 🔴 第三问(深度追问)
问:如果定位到是 GC 导致接口慢,你怎么确认和解决?
💡 答案要点
确认:
jstat -gcutil <pid> 1s看 GC 频率和耗时开启 GC 日志
-Xloggc:gc.log分析
解决:
| 问题 | 解决方案 |
|---|---|
| 频繁 Young GC | 增大年轻代大小 |
| 频繁 Full GC | 排查内存泄漏(jmap dump 分析) |
| GC 停顿时间长 | 换 G1 / ZGC 垃圾回收器 |
# 🔥 场景四:服务挂了怎么处理
# 📋 问题描述
面试官:某个微服务突然挂了,你怎么保证整个系统不瘫痪?
# 🟢 第一问
问:有哪些常见的故障转移方案?
💡 答案要点
| 方案 | 说明 |
|---|---|
| 多实例部署 | 一个挂了,其他顶上(Nacos 自动摘除) |
| 熔断 | 调用方不再调用故障服务(快速失败) |
| 降级 | 返回兜底数据(如缓存、默认值) |
| 重试 | 换个实例重试(需幂等) |
| 主从切换 | 数据库、Redis 主从自动切换 |
# 🟡 第二问(追问)
问:服务重启后,怎么保证业务不丢?
💡 答案要点
关键在于消息队列 + 持久化:
请求先写入 MQ(持久化),消费者处理完才 ACK
服务挂了,消息还在 MQ 里
服务重启后,消费者继续消费
问:服务宕机时,有没有可能已经扣了库存但没返回给用户?
# 🔴 第三问(深度追问)
服务宕机时,有没有可能已经扣了库存但没返回给用户?
有这种可能。解决方案:
订单状态机管控
定义订单状态流转,Redis 扣库存成功后先生成预订单,标记为待支付 / 待确认状态;用户最终展示的抢购结果,不由单次请求直接返回,由后台根据订单状态机统一判定兜底。
客户端重试 + 服务端幂等
客户端请求超时后允许自动重试;服务端以用户 ID + 商品 ID作为唯一幂等标识,保证同一用户同一件商品只能扣一次库存、下一次单,避免重试引发超卖和重复下单。
延迟消息定时兜底回滚(核心方案)
Redis 扣库存成功后,立即发送一条延迟消息(延迟 15~30 分钟)到 MQ:
- 正常流程:用户正常创建订单并完成支付,业务端主动消费 / 标记这条延迟消息,无需任何处理;
- 异常流程:服务宕机、流程中断或用户抢到后未付款,延迟消息到期触发消费,主动校验订单支付状态;若无有效支付记录,自动回滚 Redis 和数据库库存,释放虚占库存,解决库存冻结、数据不一致问题。