字节面经-字节跳动后端开发岗面经-19
- 轮次
- 面试合集
- 时间
- 2026-10
- 来源
- 牛客网
《面试题目》
(面经 01)
- 自我介绍。
- 哪个项目参与最深?
- 介绍第一个实习 Agent 项目和个人工作。
- 项目最难或最亮眼的地方是什么?
- 方案为何这样定?
- 错召回、漏召回解决了吗?
- 效果多少?
- 第一个实习离职原因
- 审查「DB 查工单并缓存」的代码,有什么问题?
- 一批优惠券怎么保证每人限领一张,同时不超发?
- 审查 Java 转账代码,有什么并发与原子性问题?
- 如果给转账加事务,怎么加?
- 何时需要分布式事务,有哪些方案?
- 有 AI Coding 工具后,程序员还要会写代码吗?
- 从零设计云产品智能客服:知识、资源、排障和转人工。
- 岛屿数量算法题。
- 有哪些主动发现、推动并落地的问题?
- 之前的性能问题是怎样发现并推进解决的?
(面经 02)
- 请做自我介绍
- 目前是否仍在上一段实习
- 介绍实习
- AI Coding 的前端开发与交付方式
- 前端是否主要使用 AI Coding 开发
- AI Coding 是否涉及端到端交付
- Coding:下一排列
- 理解题意并给出初步思路
- 尝试编码并解释当前卡点
- 解释 A、B、C、D 数组的用途
- 你平时代码写得多吗
- 你是本科、28 年毕业吗
- 仍在上一段实习时为什么想换机会
- 课程、学业与实习如何兼顾
《参考解析》
一批优惠券如何保证每人限领一张且不超发
两个约束要分开解决:不超发是并发扣减问题,每人限领一张是幂等 + 唯一性问题,不能靠「先查后插」这种两步操作,因为查和插之间永远有窗口。稳妥的几种做法:① 数据库唯一索引兜底——建 (活动 id, 用户 id) 的唯一索引,同时用 update coupon_stock set remain = remain - 1 where id = ? and remain > 0 的单语句原子扣减(靠行锁 + affected rows 判断成功),插入冲突就回滚扣减;② Redis 原子操作——用 Lua 脚本把「判断用户是否领过(Set 或 bitmap)+ 库存预扣减」写在一个脚本里,成功后再异步落库,落库用唯一索引保证最终不重复;③ 分段库存 / 令牌——预先按 batch 取号,减少热点 key 竞争。要主动讲清一致性代价:Redis 预扣减成功但落库失败时,要么进重试队列补偿,要么给预占加超时释放,绝不能只扣不减。幂等键必须在最外层生成并透传,否则用户连点两次就是两张券。
Java 转账代码的并发与原子性问题
经典坑有五个,审查时按这个顺序看。① 读—改—写非原子:account.setBalance(account.getBalance() - amount) 这种在并发下必然丢更新,正确做法是数据库层的 update ... set balance = balance - ? where id = ? and balance >= ?,把判断和扣减合成一条语句交给行锁;② 锁对象不对:用 synchronized(this) 或对 Integer / String 常量加锁,锁的其实不是同一个对象(Integer 缓存、字符串常量池会导致意外互斥或完全没锁住),要用与账户一一对应且全局唯一的锁对象;③ 加减顺序不一致导致死锁:A 转 B 时先锁 A 再锁 B,B 转 A 时先锁 B 再锁 A,并发下互相等待,解法是按账户 id 排序后顺序加锁,或用一条 SQL 做双向更新;④ 检查与执行分离:先 select 判余额再 update,中间可能被别的线程改掉,必须让判断条件进 where;⑤ 异常吞掉导致事务不回滚:catch 里没重新抛出,@Transactional 默认只对 RuntimeException 回滚,受检异常要显式配 rollbackFor。
转账加事务与分布式事务的适用场景
单库单表加事务很简单:方法上加 @Transactional,把扣款、入账、流水写在一个事务里,保证原子性;要注意锁的顺序(按账户 id 排序避免死锁)、事务粒度尽量小、别在事务里做远程调用和耗时操作(长事务会占着行锁)。只有跨库 / 跨服务才需要分布式事务,常见方案按一致性强度排:2PC / XA(强一致、性能差、协调者单点,生产很少用)、TCC(Try 预留、Confirm 确认、Cancel 取消,业务侵入大但性能好,适合资金类)、本地消息表 / 事务消息(先落本地消息再投递,靠重试达到最终一致,实现简单,最常用)、Saga(一串本地事务加补偿,适合长流程)、最大努力通知(只保证通知到,适合对账类)。判据是:能不能接受最终一致、有没有补偿能力、调用链路有多长;大多数业务用本地消息表就够,资金类才上 TCC。别忘了所有方案都要求幂等 + 对账兜底。
审查「DB 查工单并缓存」的代码
这类代码的问题几乎都在「缓存与数据库的关系」上,按清单挑:① 缓存 key 设计——有没有带租户 / 业务维度,会不会不同用户命中同一条数据;② 缓存穿透——查不到的工单有没有缓存空值或加布隆过滤器,否则恶意 id 每次都打库;③ 更新策略——是不是「先更新数据库再更新缓存」或「先删缓存再更新数据库」,正确姿势是先更新库再删缓存,且删除失败要有重试或 binlog 补偿;④ 过期时间——有没有设 TTL,是不是所有 key 一个时间点集体失效;⑤ 一致性窗口——同一个工单的读写在并发下会不会读到旧值,工单状态这种有状态流转的数据尤其敏感,必要时改成读主库或用版本号校验;⑥ 大 key 与序列化——工单对象是不是整个塞进缓存带了大量冗余字段;⑦ 异常处理——缓存挂了是不是直接让请求失败,正确做法是降级为直连数据库并限流;⑧ 事务边界——缓存操作被包在数据库事务里,事务回滚了缓存却写进去了。
Agent 项目的错召回与漏召回
先分清两类错误的成本:漏召回(该找的没找到)在多数场景是硬伤,用户直接拿不到答案;错召回(找来了不相关的)会让模型被噪声带偏,还可能引发生成幻觉。解决路径:① 建评测集,把「问题 → 正确的文档 / chunk」标出来,分别量化 recall@k 和 precision@k,不然优化全靠体感;② 召回阶段做混合检索——向量找语义相近、BM25 找专有名词和编号,两路取并集再 rerank;③ 切分是漏召回最大的来源,按语义 / 标题层级切、保留重叠、把标题面包屑拼进 chunk,避免答案被切断;④ 错召回靠 rerank(交叉编码器精排)、相似度阈值过滤、元数据过滤(时间、版本、业务线)收敛;⑤ 把用户 query 做改写 / 扩展,或先做一轮「意图 + 关键词抽取」再检索。回答「效果多少」时一定要给可复现的口径:评测集规模、指标定义、优化前后对比。
从零设计云产品智能客服
按「知识、资源、排障、转人工」四条线拆。① 知识:文档(产品手册、FAQ、SOP)走解析 → 切分 → 向量化入库,规范类内容做成结构化知识(资源类型、参数、限制条件),并且要有版本与生效时间,云产品文档变化快,过期知识比没有更糟。② 资源:把用户的资源清单(实例、配置、地域、账单)通过只读接口接进来,回答「我的机器为什么慢」这类问题时必须先知道「他有哪些机器」,做不到实时就把快照缓存并标注时间。③ 排障:把排障流程做成可执行的动作链——先查监控指标和事件、再比对配置基线、再看变更记录,让 Agent 按 SOP 一步步收敛,而不是一次性把上下文全塞给模型猜;每条结论都要带证据(指标截图、日志片段、变更单号)。④ 转人工:设定明确的升级条件(连续两轮未解决、涉及退款或账号权限、用户主动要求、检测到情绪激动),转接时把会话摘要、已排查项、已尝试动作一起带过去,别让用户重讲一遍。另外要有兜底:答不上就直说并引导转人工,不要硬编答案。
岛屿数量(LeetCode 200)
标准解法是网格上的连通块计数,两种都该会。DFS / BFS 洪水填充:遍历每个格子,遇到 '1' 就计数加一,然后从它出发把相连的 '1' 全部标记成 '0'(原地修改省去 visited 数组);DFS 递归写法最短,但网格很大时会爆栈,所以面试里最好主动说「大网格改用显式栈或 BFS 队列」。复杂度 O(m×n),每个格子最多进出一次。并查集:把所有 '1' 各自初始化成一个集合,向四个方向合并相邻的 '1',最后统计不同根的数量;优点是天然支持「动态加陆地」的变体(比如「岛屿数量 II」),代价是要额外维护 parent 数组且合并后不能再简单拆开。追问优化一般落在两点:空间上不额外开 visited 而原地修改;面积超大或稀疏时用方向数组代替四段重复代码,并考虑多方向剪枝(只往右下合并,每个格子最多处理两次)。
下一排列(next permutation)
考的是对「字典序」的理解,三步走,必须能背下来并解释为什么。① 从右往左找第一个降序点 i,满足 nums[i] < nums[i+1]——i 右边是递减序列,已经是它能组成的最大排列,所以必须动 i;找不到说明整个数组降序,说明已是最大排列,直接反转整个数组返回最小排列。② 从右往左找第一个大于 nums[i] 的数 j,交换 i 和 j——因为右边递减,从右往左第一个大于 nums[i] 的就是「刚好比它大的那个」,这样增幅最小。③ 把 i 右边的序列反转——交换后右边仍是递减的,反转一下就变成升序,即为该前缀下的最小排列。整体 O(n) 时间、O(1) 空间。面试官问「A、B、C、D 数组的用途」通常是在引导你把步骤拆成状态变量讲清楚,所以写代码时变量命名和注释要能对上这三步,并能举 [1,3,2]、[3,2,1] 两个边界例子。
AI Coding 之后程序员还要不要会写代码
要答成「能力迁移」而不是表态。AI 把「写」的成本压低了,但没降低「判断」的成本,反而抬高了:代码生成得越快,出错的速度也越快,没有底子的人根本分不清哪段是幻觉 API、哪段是漏了错误分支。真正被削弱的是模板化、可检索的编码劳动(写 CRUD、写样板、拼 SQL);被放大的是需求拆解、接口与数据结构设计、边界与失败路径的思考、审查与验证、故障定位、以及把业务约束翻译成可执行规格的能力。可以补一个实证式的说法:读代码、写测试、调 bug、看监控这些「验证与运维」环节的时间占比会上升,所以「会写代码」的含义变成了「能读懂、能改对、能证明它对」,而不是「能默写 API」。
AI Coding 的前端开发与端到端交付
这题考的是协作流程的成熟度。前端是 AI Coding 落地最顺的场景:组件化、状态驱动、可即时预览,所以常见形态是「描述交互与状态 → 生成组件 → 本地起 dev server 看效果 → 按反馈迭代」,配合设计系统(组件库 + token)约束输出风格,避免每个页面各长一样。端到端交付意味着 AI 的产物不只到「页面能看」,还要覆盖接口联调(mock 到真实 API 的切换)、构建打包、环境变量、部署与回滚。要做到端到端,前提是把交付流水线标准化:一条命令本地起环境、一条命令构建、CI 自动跑 lint 与类型检查、预览环境可回滚;否则 AI 生成的代码越多,越容易在集成环节堆出问题。验收上要守住三条:改动小、可回滚、有测试或可复现的验证步骤——AI 生成的代码必须自己跑过再合入,不能只看 diff 觉得对。
性能问题的主动发现与推进落地
「主动发现」靠的是可观测性 + 常态化的对比,不是等用户投诉。具体做法:① 建立基线——把核心接口的 P95 / P99 延迟、错误率、CPU / 内存 / GC、慢查询数、队列积压做成分位曲线,有基线才谈得上「异常」;② 设阈值告警并定期巡检(周报里的 TopN 慢接口、慢 SQL、增长最快的表),让问题在变成故障之前先暴露;③ 用数据定位而不是猜——链路追踪看是哪个环节耗时,火焰图看热点函数,EXPLAIN 看执行计划,profiler 看 CPU / 内存分配。推进落地的关键是把它变成一件有主、有收益、有验收的事:给出量化的影响面(影响多少请求、多少用户、每月多花多少资源)、给出最小改动的方案与回滚路径、拆成可上线的小步骤、上线后拿同一套指标做前后对比并归档结论。面试里要具体举一个自己做过的例子,说清「怎么发现 → 定位到哪一层 → 改了什么 → 指标变化」,比讲方法论有说服力。
项目「最难 / 最亮眼」怎么答、方案为何这样定
面试官问这两句是在看你有没有真正的判断力,所以回答要落在取舍而不是功能罗列。挑一个能体现你独立决策的点,按「约束 → 方案 → 代价 → 结果」四段讲:先说清当时硬约束是什么(数据量、延迟要求、人力、时间、不能停机),再说你比较了哪两三个方案、为什么排除掉别的(这是拉分的地方,只说选中的方案会显得像背的),然后坦白这个方案的代价和风险,最后给可核验的结果(指标前后对比、上线后回滚过没有、后续又迭代了什么)。「最亮眼」和「最难」可以是同一件事也可以是两件,但不要答成「用了 XX 技术」——技术是手段。被追问「方案为何这样定」时,如果当时的决策依据是信息不足下的赌博,也可以照实说清当时的判断依据,比事后合理化更可信。