字节面经-字节跳动后端开发岗面经-17
- 轮次
- 面试合集
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
(面经 01)
- 算法题:LeetCode 152 乘积最大子数组
- 请先做一个自我介绍,重点讲一下后端相关经历。
- 三个项目中,你参与最深的是哪一个?
- 原因是什么?
- 请讲一下项目整体架构,并按用户请求链路进行说明。
- 项目中的 12 个微服务是如何拆分的?
- 拆分思路是什么?
- 项目为什么同时使用 OpenFeign 和 Dubbo?
- 请介绍一下项目中的秒杀系统。
- Redis 扣库存成功后,如果后续订单创建失败,你会如何处理?
- RabbitMQ 在项目中解决了什么问题?
- 如何保证消息可靠性?
- RabbitMQ 出现大量消息积压时,处理方案是什么?
- 如果系统流量突增 10 倍,你会如何保障系统稳定?
- 请讲一下线程池的核心参数和执行流程。
- volatile 保证什么?
- 为什么无法保证 i++ 线程安全?
- ConcurrentHashMap 线程安全的原理是什么?
- JDK1.7 与 8 有哪些差异?
- Redis 为什么快?
- 单线程模型与 IO 多路复用原理是什么?
- 缓存穿透、击穿、雪崩分别是什么?
- 解决方案有哪些?
- MySQL 为什么选用 B+ 树?
- 什么是聚簇索引、回表、覆盖索引?
- 线上 SQL 耗时从 20ms 突增到 2s,排查思路是什么?
- 请设计一个 TikTok 直播间点赞系统。
- LeetCode 200:岛屿数量。
- 你有什么想要了解团队或岗位的问题?
(面经 02)
- 自我介绍
- 实习项目里,你负责了什么部分,你是独立开发还是也完成了需求对齐?
- 实习项目上线以后,减少了多少人工重复维护的任务,有具体数据吗?
- 是如何避免同步故障阻断核心业务的?
- 失败的任务,修改失败的原因一般会有哪些?
- 单号冲突检验、催办功能,催办只是发送信息吗?
- 外部服务降级是平台做的吗?
- 具体是怎么样的?
- 诊断工具开发和网易原有的工具相比怎么样?
- 你的项目里如果对消息队列进行选型和设计你会考虑哪些方面?
- 项目里如果在第一次发起请求,返回 429 错误,再切换模型时,怎么避免重复生成和重复计费?
- MCP、Skill、Harness 的理解
- 场景题:在浏览器里输入百度的域名,然后在百度搜索框输入关键词进行搜索,到最后页面显示,中间经过了哪些?
- 手撕:力扣 3 无重复字符的最长字串
(面经 03)
- RAG 项目召回的准确度有多高?
- 物流测试的文档整体有多大呢?
- 怎么再提升召回率?
- MySQL 数据库如果出现慢查询,你会怎么办?
- 讲一个你比较了解的一种设计模式在 Spring 里应用的。
- 你掌握几门语言?
- 现在让你学习新一门新语言,你会有什么路径去学习?
- 高并发内容互动营销平台这个项目里面,这里缓存是具体做什么功能的?
- 数据先存到缓存中,然后怎么同步到数据库?
- 实际的扣减,它的反馈可能是先从数据库里面加,然后再往 Redis 同步对吧?
- 如果这两个操作同时发生的话,就是会出现一些就是不一致的场景吗?
- 服务本身可能会承受很多很多流量,比如说秒杀一个商品它有 10 个库存,现在来了 1 万 QPS,你这个怎么保证你这服务的稳定性?
- 手撕:最长有效连续括号子串
(面经 04)
- 在性能测试中,你是如何与 AI 模型进行协作的?
- 使用了哪些具体的 Prompt 技巧?
- 为什么要选择 Plan-Execute-Replan 架构?
《参考解析》
秒杀:Redis 预扣减、缓存一致性、10 个库存扛 1 万 QPS
核心是把「库存」当成预占而不是扣减。Redis 用 Lua 脚本把「判断库存 + 预扣减」做成原子操作,避免高并发下的超卖;扣减成功的请求只负责把消息投进 MQ 并立刻给用户返回「排队中」,订单由消费者异步创建。如果订单创建失败,不能靠「把库存加回去」这种补救式操作,而要三段状态化:预占 → 确认 → 释放——失败进重试队列,超过上限发释放消息按幂等键回加;再叠一条超时未支付自动关单并回补库存的延时任务。缓存与数据库同步走 Cache Aside:读时先缓存后数据库再回填,写时先更新数据库、再删除缓存(不是更新缓存),删除失败靠重试或 binlog 订阅兜底。两个操作同时发生确实会不一致:读请求在「未命中 → 查库拿旧值 → 回填」的窗口里,写请求完成了更新和删缓存,旧值被写了回去;概率低的场景靠过期时间兜底,严格的场景延时双删或加版本号。
流量突增 10 倍的稳定性保障
分四层回答,从离用户近的地方往后排。① 入口层:CDN 挡住静态资源,网关按用户 / 接口 / 业务维度限流(令牌桶或滑动窗口),超限直接返回兜底页或排队页。② 应用层:无状态服务快速扩容,先把最容易被拖垮的依赖隔离(线程池或信号量),非核心链路(推荐、弹幕、个性化)降级关掉,只保下单主链路。③ 缓存层:热点数据多级缓存,防击穿用 singleflight 或互斥重建,防雪崩给过期时间加随机抖动并做预热。④ 存储层:读写分离、分库分表路由、把同步写改成消息异步化削峰。最后要落回容量摸底:压测出来的单机 QPS 上限乘机器数才是真实水位,超过就排队而不是硬扛——宁可让用户多等 200ms,也别让雪崩扩散到全站。
RabbitMQ 可靠性、积压处理与 MQ 选型
可靠性拆三段答:生产者不丢用 confirm 加本地消息表 / 事务消息,失败重发;Broker 不丢开持久化(队列持久化 + deliveryMode=2)并用镜像队列或 Quorum 队列;消费者不丢关掉自动 ack,业务成功后再手动 ack,失败进死信队列配重试与告警。消费侧必须幂等——MQ 只保证 at-least-once,重复投递是常态。积压先定位原因:消费端挂了就修并扩容;消费太慢就提并发、批量拉取、把重逻辑异步化;突发流量临时加机器并给队列设 TTL 加死信兜住过期消息;如果积压的是毒消息或超大消息,直接转死信别让它反复重投。选型维度:吞吐、延迟、顺序、投递语义、持久化与可靠性、堆积能力、延时消息、事务消息、运维成本与生态——Kafka 赢在吞吐与顺序写,RabbitMQ 赢在路由灵活和低延迟,RocketMQ 赢在事务与延时消息,Pulsar 赢在存算分离与多租户。
Java 并发:线程池、volatile 与 i++、ConcurrentHashMap
线程池七个核心参数:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。执行流程是「核心线程未满建核心线程 → 队列未满入队 → 未到最大线程数建非核心线程 → 再满触发拒绝策略」。要主动说队列选择的后果:用无界 LinkedBlockingQueue 时 maximumPoolSize 永远用不上,任务会一直堆到 OOM,生产上必须用有界队列配明确策略(CallerRunsPolicy 天然背压,AbortPolicy 配降级)。volatile 保证可见性与有序性(内存屏障禁止重排),不保证原子性。i++ 是「读—改—写」三步,两个线程可能同时读到 1 再各自写回 2,所以要用 AtomicInteger 的 CAS 或加锁。ConcurrentHashMap 1.8 是「CAS 插空桶 + synchronized 锁桶头」,读不加锁(Node 的 val 与 next 是 volatile),计数用 baseCount 加 CounterCell 分散热点;1.7 是 Segment 分段锁,并发度等于段数。整条演进主线就是锁粒度一路降低。
JDK 1.7 与 1.8 的差异
面试里几乎一定是问 HashMap 和 ConcurrentHashMap。HashMap:1.7 是数组加链表、头插法;1.8 引入红黑树(链表长度到 8 且数组长度 ≥ 64 才树化,退化阈值 6),改成尾插,扩容时用 hash & oldCap 把链表拆成高低位两条,不用重新算 hash。头插改尾插解决的是并发扩容成环导致 CPU 打满,但 1.8 并发 put 仍会丢数据。ConcurrentHashMap:1.7 用 Segment 继承 ReentrantLock 做分段锁,默认 16 段;1.8 丢掉分段锁,改成 CAS 加 synchronized 锁单个桶头,并发粒度降到桶级,并用 baseCount + CounterCell[] 分散计数热点,扩容还支持多线程协同迁移(ForwardingNode 标记已迁移的桶)。另外 1.8 的很多集合类内部从 Entry 换成了 Node,方法语义(比如 computeIfAbsent)也补齐了一批。
Redis 为什么快、IO 多路复用、缓存三大问题
快的来源有五个:纯内存操作、单线程避免锁竞争与上下文切换、基于 epoll 的 IO 多路复用、高度优化的数据结构(跳表、listpack、渐进式 rehash)、协议简单且支持 pipeline。IO 多路复用的原理是把「谁有数据可读」交给内核:epoll 用红黑树管理被监听的 fd、用就绪链表返回活跃连接,主线程只在事件循环里处理就绪连接,不必为每个连接开线程或轮询所有 fd。要补边界:Redis 6 之后网络读写改成了多线程,但命令执行仍是单线程,所以 keys、大 hgetall 这类慢命令照样阻塞整个实例。缓存三大问题:穿透是查不存在的数据,用空值缓存、布隆过滤器、参数校验挡;击穿是单个热 key 过期瞬间打穿到库,用逻辑过期、互斥重建、singleflight;雪崩是一批 key 同时失效或缓存层整体挂掉,用过期时间加抖动、多级缓存、限流熔断和预热。
MySQL 索引与慢 SQL 排查
选 B+ 树而不是 B 树或哈希,是因为它同时满足三件事:树矮(非叶子节点只存键和指针,16KB 页能放下几百个键,三层就覆盖千万级数据)、支持范围查询(叶子节点间有双向链表,between / order by 可顺序扫描)、查询稳定(数据全在叶子节点,任何一次查找 IO 次数相同)。InnoDB 的主键索引是聚簇索引,叶子节点直接放整行;二级索引叶子节点只放主键值,查非索引列时要拿主键回主键树再查一次,即回表;查询列全在索引里就不用回表,这是覆盖索引。慢 SQL 排查按「先定范围再分层收敛」:看监控定位拐点时间并与变更记录对齐 → EXPLAIN 看 type 是否掉到 ALL、key 是否选错、有没有 filesort / temporary → 常见根因是统计信息过期、隐式类型转换或对索引列做函数运算导致索引失效、数据量增长、锁等待、缓冲池命中率下降 → 先止血(加索引、改写、限流),再定位根因(拆表、加缓存、改结构),最后补监控与前后对比。
TikTok 直播间点赞系统设计
先明确量级与语义:热门直播间峰值每秒几十万次点赞,但点赞允许最终一致、容忍少量丢失,这是整套设计的起点。① 客户端聚合——前端把连点攒成批量(每 200ms 或每 20 次提交一次),从源头把 QPS 砍掉一到两个数量级,用户侧用本地计数立刻给出反馈。② 接入层削峰——网关只做鉴权与限流,把点赞写进 Kafka 这类高吞吐队列后立即返回。③ 聚合层——消费者按 roomId 分片,用 Redis INCRBY 累加增量,用分片 Set 或 HyperLogLog 做用户去重与每日次数上限校验。④ 落库与展示——增量按分钟窗口批量写回 MySQL / HBase,直播间总数从 Redis 读,弹幕与动效走另一路推送。要主动说的坑:热点房间会让单个分片打爆,所以按 roomId 哈希后再做一层本地预聚合;「一人一天只能点 N 次」这类校验必须用 Lua 或原子操作保证,不能放在应用层判。
微服务拆分、OpenFeign 与 Dubbo 并存、同步故障隔离
拆分依据是业务边界 + 变更频率 + 数据归属,不是按技术分层。讲清「按领域拆(用户、商品、订单、库存、支付、营销)、每个服务独占自己的库、服务间只通过接口通信」,再点出拆过细的代价(分布式事务、链路变长、联调成本)就够了。Feign 与 Dubbo 并存的合理解释是:Feign 走 HTTP + JSON,跨语言、易调试,适合对外或低频调用;Dubbo 走 TCP 自定义协议(3.x 也支持 Triple/gRPC),序列化紧凑、连接复用,适合内部高频调用;代价是两套注册发现与治理链路,所以按调用频次和团队习惯分组,而不是全都上。同步故障隔离的抓手是:显式超时(且上游 > 下游)、只对幂等读接口重试并配指数退避与抖动、用熔断器在失败率超阈值时快速失败、用线程池或信号量隔离不同依赖、非核心调用改异步消息、每个外部依赖都准备降级返回值。任务重试要按原因分类:超时与网络抖动可重试,参数错误和业务校验失败重试没意义,直接进人工队列。
429 切换模型时如何避免重复生成和重复计费
本质是分布式幂等 + 一次会话只允许一个有效结果。① 每次请求生成全局唯一的 requestId,把「用户 + 会话 + 轮次」当作幂等键;② 调模型前先落一条「生成中」的状态记录,重复请求先查状态,命中「生成中」就让前端重连已有流,而不是重新发起;③ 切换模型重试必须带同一个幂等键,由模型网关据此判断是否已产生过计费结果;④ 流式断点续传——已推给前端的 token 按 chunk 序号落盘,重试只在没有产出时重新生成,已产出部分既不重复生成也不重复计数;⑤ 计费以「最后一次成功结果」为准,用唯一约束(幂等键 + 状态=成功)在库层面挡住重复写入,失败的调用单独记账做对账。一句话总结:重试的是调用,不是结果。
AI 工程:RAG 召回率、MCP/Skill/Harness、Plan-Execute-Replan 与 ReAct
RAG 召回率先量化再优化:建「问题 → 正确 chunk」的评测集,分开看召回率与准确率,并报清文档规模(篇数、原始格式、切分后 chunk 数、总 token)。提升顺序是:按语义或标题层级切分并留重叠、向量加 BM25 的混合检索(专有名词靠关键词)、query 改写与 HyDE、加 rerank 精排、元数据过滤、多带几条邻接 chunk。召回上不去时先查数据有没有进库、切分有没有把答案切断,再谈换模型。MCP 是模型与外部工具之间的标准协议,解决工具如何被统一描述与调用;Skill 是写给模型的任务级作业指导(流程、知识、约束),本身不提供能力;Harness 是包在模型外面的运行外壳——循环、状态、上下文装配、工具执行、重试、预算与观测。ReAct 边想边做、每步依赖上一轮观测,灵活但长任务易跑偏、上下文膨胀快;Plan-Execute-Replan 先出计划再分步执行,失败时带着失败证据回到计划层重规划,适合步骤多、依赖强、可验收的任务,代价是多一次规划开销、对环境变化的反应慢一些。
浏览器从地址栏到搜索结果页的完整链路
分层说才不会漏。① URL 解析与本地缓存检查:查浏览器缓存、Service Worker、DNS 缓存,HSTS 列表决定是否强制 https。② DNS 解析:本地 hosts → 系统缓存 → 递归解析器 → 根域 → .com 顶级域 → baidu.com 权威服务器,拿到 IP(常见 CDN 的 CNAME 再到真实源)。③ 建立连接:TCP 三次握手,若为 https 再走 TLS 握手(ClientHello、证书链校验、密钥协商、Finished);HTTP/2 或 HTTP/3 会复用连接或直接走 QUIC。④ 发请求:带 Cookie、UA、Accept 等头,经 CDN / 负载均衡 / 网关到应用服务器,命中的缓存直接返回。⑤ 服务端处理:路由、鉴权、查询(缓存加数据库)、渲染 HTML 后响应。⑥ 浏览器渲染:解析 HTML 建 DOM、解析 CSS 建 CSSOM、合成渲染树、布局、绘制、合成上屏,JS 可能阻塞解析并触发二次渲染。⑦ 输入关键词搜索:前端再发一次异步请求到搜索接口,服务端走查询理解、召回、排序后返回结果,前端渲染结果页,同时埋点上报。