针对业务难点和分布式系统的边界问题进行提问
- 责任链是怎么实现的?用了哪些 Spring 注解?
- RocketMQ 延时消息底层是怎么实现的?如果消息积压了怎么办?
- BinLog 同步架构是怎么样的?如果 RocketMQ 挂了或者消息丢失了怎么保证数据一致性?(涉及到MQ的可靠性保证)
- Lua 脚本具体是怎么写的?为什么 Lua 能保证原子性?如果是Redis集群模式 Lua 脚本有什么坑?
12306购票本质上是极其复杂的秒杀系统,一定会问对“库存”和“并发”的理解。
- 如何防止“超卖”问题?
- 追问: 你的系统是在哪一步扣库存的?(下单扣还是支付扣?)这两种方式各有什么优缺点?
- 追问: 高并发下,如何保证最后几张票不被并发请求同时买走?(考察 Redis 预扣减、Lua 原子性、MySQL 乐观锁/悲观锁等)。
- 12306 的库存扣减和普通电商秒杀有什么区别?
- 提示: 这是个区分度极高的问题。电商是总数扣减,而火车票是区间扣减(比如买北京到上海的票,中间所有站的可用库存都要减 1)。你的系统里“区间库存”是怎么设计和计算的?
- 黄牛刷接口怎么防?
- 提示: 接口防刷设计(IP 限流、验证码、单用户购买频率限制、隐藏秒杀接口地址等)。
- 缓存穿透、击穿、雪崩在你的系统里是如何避免的?
- 追问(击穿): 热门车次突然开售,瞬间大量请求打过来,Redis 里还没缓存(或者刚好过期),怎么防止 MySQL 被打挂?(考察互斥锁/Redisson、热点数据预热)。
- BinLog + MQ 保证缓存一致性的极端情况
- 追问: 如果你刚更新了 MySQL,Binlog 还没发出去,缓存里的旧数据此时被读到了,出现“短暂的脏数据”业务能接受吗?(考察对“最终一致性”和“强一致性”的理解)。
- 追问: MySQL 采用了什么事务隔离级别?对 BinLog 同步有影响吗?
- 表结构设计与优化
- 追问: 你的订单表和车票表是怎么设计的?数据量级达到千万级以上时,查询变慢了,有什么优化思路?(考察索引设计、分库分表 ShardingSphere 的理论)。
用 MQ 做了异步解耦和延时取消,会重点考察“消息不丢”和“消息不重复”。
- 如何保证 RocketMQ 消息的绝对不丢失?
- 提示: 需要从 生产者(事务消息/Confirm机制)、Broker(同步刷盘、主从同步)、消费者(手动ACK)三个维度来回答。
- 消费者幂等性设计
- 追问: 因为网络抖动,MQ 给消费者投递了两次“取消订单”的消息,你怎么保证订单不会被异常处理两次,库存不会被加两次?(考察数据库唯一索引、Redis 防重 Token、状态机判断等幂等性方案)。
- 消息堆积怎么处理?
- 追问: 如果抢票高峰期,RocketMQ 里堆积了 10 万条发往 MySQL 的写请求,消费端处理不过来了,应该怎么排查和解决?
- 责任链模式的扩展
- 追问: 如果我现在要新增一个“学生票资质校验”的逻辑,在你的代码里具体需要改动几处?(考察开闭原则是否真正在代码里落实)。
- 追问: 责任链里的校验节点,如果某个节点抛出了异常,你是如何统一捕获并返回给前端优雅的错误码的?(考察 SpringBoot 全局异常处理
@RestControllerAdvice)。
除了前面提到的高并发、消息队列和缓存一致性,面试官还会从底层原理、分布式架构细节以及具体业务算法这三个维度继续深挖。
以下是补充的进阶高频面试题,主要针对大厂二面/三面的深度:
- 座位分配算法是怎么实现的?
- 追问: 连座(比如A、B、C三人一起买,要求坐在一起)是怎么分配的?如果连座不够了怎么降级处理?
- 追问: 你的座位状态在 Redis 中是用什么数据结构存储的?(考察 Bitmap 或 Hash 的高级应用,12306常用 Bitmap 来表示座位的占用状态)。
- 多终端重复提交问题(防重)
- 追问: 同一个用户,网络卡顿连点了两次“提交订单”,怎么保证数据库不会生成两笔相同的订单?(考察前端按钮防抖 + 后端防重 Token 机制 / 数据库防重表)。
- SpringBoot 异步与线程池
- 追问: 你提到系统支持大量用户请求,SpringBoot 内置的 Tomcat 默认最大连接数和线程数是多少?有调优过吗?
- 追问: 责任链校验或者异步发 MQ 时,用到了自定义线程池吗?核心线程数和最大线程数是怎么评估的?拒绝策略选了哪个?
- Spring 事务失效场景(高频必问)
- 追问: 你的下单逻辑肯定加了
@Transactional吧?如果在下单逻辑里,你在 try-catch 里面捕获了异常并且没有抛出,事务还会回滚吗? - 追问: 如果在同类中,一个非事务方法调用了事务方法,事务会生效吗?(考察 Spring AOP 动态代理机制)。
- 追问: 你的下单逻辑肯定加了
- 分布式锁的应用
- 追问: 你是用 Lua 脚本做的限流和令牌分配,那在“定时任务回滚超时订单”时,如果你的后台部署了多台机器,怎么保证同一笔订单不会被两台机器同时回滚?(考察 Redis/Redisson 分布式锁、或者 MQ 广播/集群消费模式的理解)。
- 限流算法的深挖
- 追问: 你提到使用 Redis Lua 完成“令牌分配限流”,你用的是令牌桶算法还是漏桶算法?这两者在应对突发流量时有什么区别?
- 追问: 如果限流触发了,前端用户看到的是什么?(排队中?还是直接报错提示“系统繁忙”?)
- 扣库存的并发控制
- 追问: 虽然你用了 Redis 预扣减库存,但最终还是要落库的。在 MySQL 中执行
UPDATE stock SET count = count - 1 WHERE id = ?时,MySQL 底层是怎么加锁的?(考察行级锁、排他锁)。 - 追问: 如果这个 SQL 走不到索引,会发生什么?(考察行锁升级为表锁,导致整个车次不可买的严重事故)。
- 追问: 虽然你用了 Redis 预扣减库存,但最终还是要落库的。在 MySQL 中执行
面试官的套路通常是:顺着你的亮点 -> 找一个极端场景(比如机器宕机、网络延迟、并发冲突) -> 问你系统会怎样 -> 问你怎么解决。 准备时多问自己几个“如果XXX挂了怎么办?”。
针对大厂的面试流程,一面通常侧重于“基础与实现细节”(你是怎么写代码的,底层原理懂不懂),而二面通常侧重于“架构设计、极端场景与技术选型(Trade-off)”(为什么要这么选,系统出大故障了怎么办)。
一面面试官会盯着你的代码细节和使用的组件底层原理提问:
-
关于 Redis Lua 的底层与坑
- 深挖: Lua 脚本在 Redis 中执行时,如果脚本写得有问题死循环了,会对 Redis 造成什么影响?(考察 Redis 单线程模型,会导致整个 Redis 阻塞)。
- 深挖: 在 Redis Cluster(集群模式)下,Lua 脚本操作多个 key 时有什么限制?(考察 Hash Tag 机制,确保多个 key 落在同一个 Slot)。
-
关于责任链模式的代码落地
- 深挖: 你的责任链节点(Handler)是交由 Spring 管理的单例 Bean 吗?如果是,里面能保存用户请求的局部变量(状态)吗?(考察 Spring 单例 Bean 的线程安全问题,通常配合
ThreadLocal传递参数)。 - 深挖: 你是怎么把这些责任链节点按顺序组装起来的?(考察
@Order注解,或者通过配置类/List注入实现动态组装)。
- 深挖: 你的责任链节点(Handler)是交由 Spring 管理的单例 Bean 吗?如果是,里面能保存用户请求的局部变量(状态)吗?(考察 Spring 单例 Bean 的线程安全问题,通常配合
-
车票查询的 MySQL 索引设计
- 深挖: 用户查询车票通常是条件:
出发站 + 到达站 + 出发日期。针对这个查询,你的 MySQL 联合索引是怎么建的?(考察最左前缀匹配原则)。 - 深挖: 如果有人只输入
出发日期查询,这个联合索引还能走得通吗?(考察索引失效场景及优化)。
- 深挖: 用户查询车票通常是条件:
-
RocketMQ 延时消息底层
- 深挖: RocketMQ 的延时消息底层是怎么实现的?(考察 RocketMQ 的 18 个延时级别,或者是 RMQ 5.0 的基于时间轮的任意时间延时)。
二面面试官(通常是架构师或部门主管)会假设系统面临百万级流量或机器大面积宕机,考察你的全局观:
-
技术选型与 Trade-off(妥协与权衡)
- 深挖: 你的消息队列为什么选 RocketMQ 而不是 Kafka 或 RabbitMQ?(考察对各 MQ 特性的理解:RocketMQ 支持延时消息、事务消息,且对金融级/订单级业务场景支持更好)。
- 深挖: 你的缓存一致性方案选了
Binlog + MQ异步更新。为什么不直接在代码里更新数据库 + 删除缓存(Cache Aside 模式)?你的方案带来了什么额外成本?
-
系统的高可用与故障降级(非常爱问)
- 深挖: 抢票最高峰时,如果你的 Redis 突然挂了(或者缓存雪崩了),系统该如何自救?(考察降级策略:限流直接返回前端繁忙、依靠本地缓存、或者直接暂停售票,绝对不能让流量打穿到 MySQL)。
- 深挖: 监听 Binlog 的组件(比如 Canal)如果宕机了几个小时,重启之后,怎么保证 Redis 和 MySQL 的数据重新对其?(考察全量同步与增量同步的结合)。
-
系统的性能瓶颈排查
- 深挖: 如果系统上线后,发现购票接口的响应时间(RT)从 50ms 飙升到了 2000ms,你会从哪些方面去排查?(考察监控排查思路:看 CPU/内存占用 -> 看 JVM 是否频繁 Full GC -> 看 MySQL 是否有慢 SQL/锁等待 -> 看网络带宽等)。
-
可观测性与全链路追踪
- 深挖: 一笔订单在你的系统里流转了 Redis、MySQL,还发了 MQ。如果用户反馈“订单状态不对”,你怎么快速在日志里把这一整条调用链找出来?(考察对 TraceId、MDC 机制或分布式链路追踪 Skywalking/Zipkin 的了解)。
应对策略: 一面的问题你要能画出底层图或写出伪代码;二面的问题如果没有真实经验,可以说:“在我的个人项目中暂未实现这个监控组件,但如果在企业级生产环境中,我会采用 XXX 方案来解决。” 展现你的技术视野。
从另外几个核心技术维度(分布式事务、海量数据存储、JVM调优、网络与安全交互)出发的面试连环炮:
虽然是个人项目,但12306这种级别的系统必然是拆分微服务的,面试官会假设你的系统是分布式的:
-
跨服务的分布式事务如何保证?
- 问题: 如果“创建订单”和“扣减积分/扣减优惠券”在两个不同的服务(或不同的数据库)里,如何保证它们要么同时成功,要么同时失败?
- 考察点: 对 Seata(AT/TCC 模式)、或者 RocketMQ 事务消息(半消息机制)的理解。
-
支付成功后的状态流转
- 问题: 用户支付成功后,第三方支付平台(如支付宝/微信)回调你的系统,此时你怎么通知前端页面刷新支付状态?
- 考察点: 客户端长轮询(Polling)、WebSocket 实时推送、或是 SSE(Server-Sent Events)机制的选型对比。
12306 的数据量是极其庞大的,面试官会考你面对数据膨胀时的处理思路:
- 订单数据的冷热分离方案
- 问题: 随着系统运行,订单表数据量达到亿级,查询变得非常慢。对于“已经乘车完成”的几个月前的历史订单,怎么处理?
- 考察点: 冷热数据分离策略、定时任务数据归档、Elasticsearch 宽表查询历史数据。
- 分库分表后的复杂查询挑战
- 问题: 如果订单表按照
user_id进行了分表,现在管理员后台需要“根据车次号(train_id)查询某个车次的所有购买订单”,这个时候怎么查? - 考察点: 异构索引表、Binlog 同步到 ES 或数据仓库解决多维查询问题。
- 问题: 如果订单表按照
大并发流量下,对 Java 虚拟机的考验极大:
- 高并发下的 JVM 垃圾回收挑战
- 问题: 秒杀抢票瞬间,会产生大量的“订单实体对象”,这会导致 JVM 发生什么?如果发现频繁触发 Full GC,你会调整哪些 JVM 参数?
- 考察点: 年轻代(Eden/Survivor)大小调整、老年代阈值设置、G1/CMS 收集器的理解,避免对象过早晋升老年代。
- 本地内存的高效使用
- 问题: 如果让你用一台机器的本地内存(不用 Redis)做库存拦截,你会用什么数据结构或并发类来抗住百万并发扣减?
- 考察点: Java
JUC包的理解。回答LongAdder(分段锁思想)会比回答AtomicInteger或synchronized惊艳很多。
大厂非常看重数据安全,尤其是涉及12306实名制的业务:
- 敏感信息的存储与查询
- 问题: 用户的身份证号、手机号在 MySQL 里是明文存储的吗?如果做了加密(如 AES 加密),那用户登录时想用手机号进行等值查询该怎么查?(加密后的密文变了怎么办?)
- 考察点: 静态脱敏/动态脱敏、哈希索引字段、或者基于分词搜索的设计。
- 密码存储安全
- 问题: 会员注册时,密码在数据库是怎么存的?为什么不能只用简单的 MD5?
- 考察点: 密码加盐(Salt)哈希存储(如 BCrypt 算法),防彩虹表攻击。
- 内存淘汰策略的选择
- 问题: 抢票期间产生的大量车次缓存,如果把 Redis 的内存打满了会怎样?你的项目里 Redis 配置了哪种内存淘汰策略?为什么?
- 考察点: 对
volatile-lru、allkeys-lru等策略的理解,结合业务(比如只淘汰设置了过期时间的缓存)。
- Redis 持久化策略的取舍
- 问题: 购票令牌或库存数据如果在 Redis 里,突然 Redis 服务器断电重启了,数据怎么恢复?RDB 和 AOF 哪种方式更适合你的抢票系统?
- 考察点: RDB 丢失数据风险 vs AOF 体积过大恢复慢,引出 Redis 4.0 之后的混合持久化方案。
将视角切换到数据库架构进阶、前端与网关交互、极端业务边界情况、以及框架底层运行机制。
- 主从同步延迟导致的数据“幻读”
- 问题: 如果你的 MySQL 做了主从分离(写主库,读从库),用户下单成功(写主库),页面立刻跳转到“我的订单”列表(读从库)。由于主从同步有几十毫秒的延迟,用户没看到刚买的订单,以为没买成功又去下了一单。怎么解决这个体验和逻辑问题?
- 考察点: 强制路由主库读、或者结合 Redis 缓存过渡、或者前端延迟查询机制。
- 深度分页的性能灾难
- 问题: 运营后台需要查看历史订单,如果执行类似
SELECT * FROM orders WHERE ... LIMIT 1000000, 20的深度分页,MySQL 会卡死。你的系统怎么解决这种查询? - 考察点: 游标分页(记录上一页最大 ID)、延迟关联(先查主键再回表)。
- 问题: 运营后台需要查看历史订单,如果执行类似
- 取消订单与用户主动支付的并发冲突
- 问题: 订单 30 分钟未支付会自动取消(触发了 RocketMQ 延时消息)。假设在第 29 分 59 秒,用户按下了支付按钮。你的后台在处理支付成功的同时,MQ 刚好也在执行取消订单的逻辑。怎么保证最终状态不错乱?
- 考察点: 状态机设计(State Machine)以及数据库的乐观锁更新(如
UPDATE order SET status = 'PAID' WHERE id = ? AND status = 'UNPAID')。
- 换乘与智能推荐(中转票计算)
- 问题: 如果直达的票卖完了,12306 会推荐“A站 -> C站 -> B站”的中转方案。如果在你的系统里要实现这个功能,底层数据和算法该怎么设计?
- 考察点: 图计算算法(最短路径/Dijkstra)、或者提前通过离线任务(定时任务)把常见的中转路线计算好放入缓存(空间换时间)。
- 秒杀绝对时间的同步问题
- 问题: 抢票是早上 8:00 开售。如果用户的手机时间比北京时间快了 1 分钟,他在 7:59 就看到了售卖按钮并狂点,你的系统会怎样?怎么保证所有用户的起跑线是一致的?
- 考察点: 前端不信任本地时间,必须向后端请求统一的服务器时间;网关层过滤未到时间的非法请求。
- CDN 与静态资源下沉
- 问题: 抢票瞬间,除了抢票接口,查询车次列表、加载车站图片也会有百万次请求。你的 Tomcat 能扛住这些流量吗?
- 考察点: 动静分离。车次列表缓存到 Nginx 或 CDN,甚至推送到客户端本地缓存,后端只处理真正的“核心动态交易接口”。
- 顺序消息的保证
- 问题: 假设某个业务场景要求订单的“创建”、“支付”、“发货”这三条 MQ 消息必须被消费者按顺序处理,RocketMQ 怎么保证这一点?
- 考察点: 全局顺序消息 vs 局部顺序消息(MessageQueueSelector 基于 OrderId 进行 Hash,确保同一个订单发到一个 Queue 里)。
- 死信队列(DLQ)的兜底
- 问题: 如果消费者处理取消订单的消息一直报错(比如数据库宕机了),RocketMQ 会一直重试吗?重试达到了最大次数还没成功怎么办?
- 考察点: 死信队列机制。重试 16 次失败后进入 DLQ,需人工介入或写专门的报警补偿脚本。
- Spring Boot 自动装配原理(必考基础)
- 问题: 你在项目里引入了 Redis 和 RocketMQ,只需要写几个配置项就能用了。Spring Boot 底层是怎么帮你把这些 Bean 实例化出来的?
- 考察点:
@EnableAutoConfiguration、spring.factories/org.springframework.boot.autoconfigure.AutoConfiguration.imports、以及@Conditional条件注解。
- 平滑发布与优雅停机
- 问题: 你的系统正在运行中,此时你需要发布一段新代码重启服务器。怎么保证那些刚刚发起了“下单请求”、还在内存里处理一半的线程不会被强制杀死?
- 考察点: Spring Boot 的优雅停机(Graceful Shutdown)配置、从注册中心先下线再处理残余请求的机制。
几个新的问题,集中在微服务治理、容器化部署、特殊数据结构以及后台复杂操作:
- 配置动态刷新机制
- 问题: 如果抢票高峰期,你需要紧急把限流的阈值从 1000 调小到 500。除了重启服务,系统能实现动态刷新吗?Nacos/Apollo 底层是怎么通知到你机器上的 Spring 环境变量的?
- 考察点: 长轮询机制(Long Polling)、
@RefreshScope注解底层原理(代理对象的重新创建)。
- 服务注册与发现的心跳风暴
- 问题: 如果你的服务节点有上万个,大家都向 Nacos 发送心跳包,Nacos 怎么抗住这种网络压力?
- 考察点: Nacos 的客户端本地缓存(推拉结合模型)、AP 架构与 CP 架构在服务发现中的取舍。
- 海量 UV 的统计
- 问题: 12306 首页每天有上亿次访问,如果运营产品经理需要统计今天的独立访客数(UV),要求误差在 1% 以内且极度节省内存,你会怎么做?
- 考察点: Redis 的
HyperLogLog数据结构及其底层概率统计算法。
- 地理位置与附近车次(LBS)
- 问题: 如果系统新增一个功能:“查询我当前位置距离最近的高铁站”,在数据库或 Redis 中该用什么技术实现?
- 考察点: Redis 的
GEO数据结构,或者 MySQL 8.0 的空间索引,或者 Elasticsearch 的 Geo 搜索。
- 海量数据的导出(OOM 问题)
- 问题: 财务人员在后台点击了“导出今年所有的交易流水(共计 2000 万条记录)”到 Excel。如果直接查数据库并用 POI 写入 Excel,服务器必定内存溢出(OOM)。怎么设计这个导出功能?
- 考察点: 异步导出(生成任务放入队列表)、分页流式读取 MySQL(
fetchSize)、使用阿里EasyExcel等低内存占用的组件。