恒生后端一面:秒杀超卖、Redis库存恢复与MySQL索引
- 轮次
- 一面
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
- 详细说一下「本地生活内容社区」这个项目:大概几个人在做,你在其中是什么角色,主要负责哪些内容?
- 秒杀过程中防止超卖,你做了哪些思考和设计?
- 假设 Redis 的库存扣减完之后 Redis 挂了,再把它拉起来时,库存信息是从哪里加载的?
- 除了秒杀超卖的场景,这个项目你觉得还有哪些复杂点?
- SQL 的性能优化之前有没有接触和学习过?
- 这个本地生活社区项目大概设计了多少张表?为什么这么设计?核心的表有哪些?
- 用户表有哪些字段,是用一张表保存的吗?商户表呢?
- 什么原因可能导致索引失效?百分号在左边为什么就走不到索引?
- MySQL 的 B+ 树有什么优缺点?
- 个人 1~2 年的职业规划是什么样子的?
《参考解析》
项目题怎么答:先给结构,再给取舍
一面开场基本都是项目,面试官真正想确认的是「这个项目你能不能讲清楚」。可以按「一句话背景 → 团队规模与我的角色 → 我负责的模块 → 一个最难的点及取舍 → 结果」来组织,主动给出量级(数据量、QPS、表数量),比笼统地说「用了 Redis 和 MQ」有说服力。被追问「还有哪些复杂点」时不要只盯秒杀:本地生活社区真正的复杂度往往在内容与商户的多维查询、冷热数据分层,以及订单库存与内容互动之间的一致性上,挑你真做过的一条讲透即可。职业规划那题不必背模板,说清「先把当前技术栈做深、能独立负责一个模块」这类与岗位匹配的方向就够。
秒杀防超卖与 Redis 库存的可靠性
防超卖的关键是把「判断余量」和「扣减」合成一个原子动作:在 Redis 里用 Lua 脚本扣库存(或 DECR 后判负回补),扣成负数就直接拒绝,避免「先查再扣」之间被并发插队;Redis 只承担预扣与限流,真实下单经 MQ 异步削峰落到 DB,再由数据库的唯一约束或乐观锁兜底,才敢说不会超卖。追问「Redis 挂了再拉起来库存从哪来」是同一道题的下半段:Redis 是缓存不是账本,权威数据始终在 DB,重启后应以 DB 库存为准重建,并把未落库的在途订单一起算进去,必要时跑一次对账把两边拉平。工程上还要想清楚宕机窗口内的请求是放行还是直接失败(宁可少卖不能超卖)、扣减成功但 DB 写失败如何回补,以及用本地库存和限流把打挂 Redis 的流量挡在前面。AOF/RDB 只能缩小丢失窗口,不能当作「不会丢」的保证。
表设计与索引:从字段一直问到 B+ 树
「设计了多少张表、为什么」考的是你有没有真建过库:通常按领域拆成用户、商户、内容、互动、订单若干组表,回答时给出主干再点一两条关键设计(互动类大表拆分、热点字段冗余),比报一个数字有效得多。用户表与商户表分开是常规做法,两者生命周期和字段差异很大,商户还要放资质、结算等信息,硬塞一张表会带来大量空列;是否再细分基本信息与扩展表取决于访问频次,不必为「规范」过度拆。索引失效常见于:对索引列做函数或运算、隐式类型转换、or 两侧不全有索引、复合索引不满足最左前缀,以及优化器估算后认为全表扫更便宜。左模糊 like '%x' 走不到索引,是因为 B+ 树按键值前缀有序,前缀未知就无法定位扫描起点,只能退化成全表或全索引扫描。B+ 树矮胖、非叶节点只存键因而单次 IO 能覆盖更多行、叶子链表让范围查询和排序很顺,代价是写入可能页分裂、非聚簇索引要回表。SQL 优化这题答「没接触过」很吃亏,至少要说得出慢查询日志定位、explain 看 type 与 key、建合适索引或覆盖索引、避免 select * 和大偏移分页。