字节跳动后端开发面经 · 2025
岗位:后端开发工程师(抖音直播)
背景:211 硕士,Java 为主,1 段大厂实习 + 1 段开源贡献
结果:OC,总包 38w+(Base + 期权 + 房补)
时间线:9.1 投递 → 9.6 一面 → 9.20 四面 → 9.26 OC
字节面试风格(2025 版)
相比 2024 年的感受,2025 字节后端面试有几个明显变化:
- 算法仍然硬核:但更偏向「模板 + 变体追问」,滑动窗口、图论成了新的高频区。
- 项目深挖更体系化:面试官会把你实习的项目当生产系统来拷问,量化指标要求更高。
- 系统设计比重上升:中后期几乎必出,且会追问可靠性、限流、去重等细节。
- 语言细节抽查:Java 八股虽不如算法重要,但集合源码、并发工具是送分题,必须拿稳。
整体感觉:算法决定下限,项目与系统设计决定上限。
一面(9.6):算法 + Java 基础
面试官:抖音直播基础架构组
时长:60 分钟
算法题 1:最小覆盖子串
LeetCode 76。直接考察滑动窗口模板的熟练度。
我按「扩大 → 收缩 → 更新」三步写,need/window 双哈希 + valid 计数:
public String minWindow(String s, String t) {
Map<Character, Integer> need = new HashMap<>();
for (char c : t.toCharArray()) need.put(c, need.getOrDefault(c, 0) + 1);
Map<Character, Integer> window = new HashMap<>();
int left = 0, valid = 0, start = 0, len = Integer.MAX_VALUE;
for (int right = 0; right < s.length(); right++) {
char c = s.charAt(right);
if (need.containsKey(c)) {
window.put(c, window.getOrDefault(c, 0) + 1);
if (window.get(c).equals(need.get(c))) valid++;
}
while (valid == need.size()) {
if (right - left + 1 < len) { start = left; len = right - left + 1; }
char d = s.charAt(left);
if (need.containsKey(d)) {
if (window.get(d).equals(need.get(d))) valid--;
window.put(d, window.get(d) - 1);
}
left++;
}
}
return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
}
追问:
- “为什么
valid用need.size()而不是t.length()?” → 目标字符可能重复,need的 key 数才是覆盖条件。 - “如果字符集是英文小写,怎么优化?” → 用
int[128]数组替代 HashMap,常数更快。 - “给你一个全是正数的数组,求最短子数组和 ≥ target(LC 209)?” → 同样滑动窗口,
while收缩条件换成和。
算法题 2:无重复字符的最长子串
LeetCode 3。变长窗口,
Set判重。
public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<>();
int left = 0, ans = 0;
for (int right = 0; right < s.length(); right++) {
while (set.contains(s.charAt(right))) set.remove(s.charAt(left++));
set.add(s.charAt(right));
ans = Math.max(ans, right - left + 1);
}
return ans;
}
Java 基础(15 分钟)
- HashMap 底层:数组 + 链表 + 红黑树(≥8 转树),put 流程、负载因子 0.75、扩容机制。
- ConcurrentHashMap:
CAS + synchronized锁桶,size 用CounterCell累加。 - String 不可变的原因:安全、哈希缓存、线程安全、常量池复用。
二面(9.11):项目深挖 + 算法
面试官:抖音直播弹幕服务负责人
时长:75 分钟
项目深挖(35 分钟)
我的实习项目是直播间弹幕去重与频控服务,面试官直接按生产系统标准连环追问:
“弹幕高峰期 QPS 多少?怎么压测出来的?”
- 峰值约 8 万 QPS,用 JMeter 分布式压测 + 线上真实流量回放验证。
“同一用户短时间内连发相同弹幕,怎么去重?”
- Redis
SETNX+ 时间窗口(5 秒内重复内容直接丢弃)。 - 追问"窗口是滑动窗口还是固定窗口?" → 固定窗口简单但有边界问题,我用了 Redis Sorted Set 做滑动窗口计数。
- Redis
“弹幕要有序展示,怎么保证时序?”
- 按服务端接收时间打单调递增 ID(雪花算法),客户端按 ID 排序。
- 追问"同一秒内大量弹幕,雪花 ID 并发够吗?" → 位段分配 + 队列化消费。
“如果有人刷屏(灌水弹幕)怎么办?”
- 单用户频控(令牌桶,按用户维度)+ 全局内容黑名单 + 命中后降级静默丢弃。
“如果 Redis 挂了,你的去重逻辑怎么兜底?”
- 降级到本地内存窗口(单机限流)+ 数据库唯一索引兜底,Redis 恢复后补齐。
算法题:滑动窗口最大值
LeetCode 239。要求 O(n)。单调队列维护窗口内最大值。
public int[] maxSlidingWindow(int[] nums, int k) {
Deque<Integer> q = new ArrayDeque<>(); // 存下标,队首是窗口最大值
int[] ans = new int[nums.length - k + 1];
for (int i = 0; i < nums.length; i++) {
while (!q.isEmpty() && nums[q.peekLast()] <= nums[i]) q.pollLast();
q.offerLast(i);
if (q.peekFirst() <= i - k) q.pollFirst();
if (i >= k - 1) ans[i - k + 1] = nums[q.peekFirst()];
}
return ans;
}
追问:“如果改为求滑动窗口中位数?” → 用两个堆(大顶堆存小半、小顶堆存大半)或有序集合,复杂度 O(n log k)。
三面(9.16):算法 + 系统设计
面试官:抖音直播中台架构师
时长:80 分钟
算法题 1:课程表 II(拓扑排序)
LeetCode 210。Kahn 算法按入度剥离,同时检测环。
public int[] findOrder(int numCourses, int[][] prerequisites) {
List<List<Integer>> graph = new ArrayList<>();
int[] in = new int[numCourses];
for (int i = 0; i < numCourses; i++) graph.add(new ArrayList<>());
for (int[] p : prerequisites) { graph.get(p[1]).add(p[0]); in[p[0]]++; }
Queue<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numCourses; i++) if (in[i] == 0) q.offer(i);
int[] order = new int[numCourses];
int idx = 0;
while (!q.isEmpty()) {
int cur = q.poll();
order[idx++] = cur;
for (int nxt : graph.get(cur))
if (--in[nxt] == 0) q.offer(nxt);
}
return idx == numCourses ? order : new int[0]; // 有环返回空
}
追问:“DFS 三色标记怎么写?判环的依据?” → 0 未访问 / 1 访问中 / 2 已完成,遇到访问中节点即有环。
算法题 2:二叉树的最近公共祖先
LeetCode 236。后序递归,左有右有则当前为 LCA。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) return root;
return left != null ? left : right;
}
系统设计:通知系统
要求设计一个支持亿级用户、多平台推送、防打扰的通知系统。这是 2025 年字节很喜欢考的一道题(App 弹窗/站内信/邮件都属于它)。
我的回答框架:
- 需求澄清:用户量 2 亿、峰值 50 万 QPS、渠道含推送/站内信/短信/邮件、分钟级延迟可接受、不能丢消息。
- 架构分层:API 网关 → 通知服务(落库 + 入队)→ Kafka 按渠道分区 → 渠道分发器 → 推送 worker / 站内信 / 邮件。
- 消息模型:模板 + 参数,多渠道独立渲染,业务方只管发模板 ID。
- 去重与限流:单用户频控(Redis 计数)+ 内容去重(
SETNX+ 时间窗)+ 客户端静默聚合。 - 可靠性:消息状态机
PENDING → SENT → FAILED,指数退避重试 + 幂等消费(全局消息 ID 去重)+ 死信队列告警。 - 扩展:渠道降级(推送失败补发短信)、token 失效清理(APNs 410)、营销错峰。
追问:
- “验证码这种高优消息和营销消息混在一起怎么办?” → 优先级队列,Kafka 分区 + 消费端高优先取;堆积时优先丢弃低优营销。
- “同一用户在 10 秒内收到 20 条推送怎么处理?” → 单用户限流 + 客户端聚合为一条。
- “推送 token 失效怎么发现?” → 推送返回
410 Gone时从设备表删除,避免反复无效投递。
四面(9.20):交叉面 + 算法
面试官:抖音集团基础技术部
时长:70 分钟
算法题:冗余连接(并查集)
LeetCode 684。加入一条边时若两端已在同一连通分量,则该边成环、即为答案。
int[] parent;
int find(int x) { return parent[x] == x ? x : (parent[x] = find(parent[x])); }
public int[] findRedundantConnection(int[][] edges) {
int n = edges.length;
parent = new int[n + 1];
for (int i = 1; i <= n; i++) parent[i] = i;
for (int[] e : edges) {
int a = find(e[0]), b = find(e[1]);
if (a == b) return e;
parent[a] = b;
}
return new int[0];
}
追问:“路径压缩和按秩合并的作用?” → 把查找均摊到 O(α(N));按秩合并防止树退化。
交叉提问(Java 并发 + 系统)
- ReentrantLock 和 synchronized 区别:公平性、可中断、Condition 多条件、锁升级(偏向 → 轻量 → 重量)。
- 线程池参数与拒绝策略:核心/最大/队列、
AbortPolicyvsCallerRunsPolicy的场景选择。 - “你的弹幕服务如果 QPS 翻 10 倍,哪里先成为瓶颈?” → 先 Redis 频控的热点 key,再 Kafka 分区数,最后是存储写入。
五面(9.22):HR 面
时长:35 分钟
- 为什么投字节?对抖音直播的了解?
- 实习中最大的成长与最失败的决策?
- 手上其他 offer?薪资期望?
- 反问:直播团队技术栈(Java + Go 混合)、新人 6 个月期望产出。
HR 面套路稳定,重点是别暴露无逻辑的回答。遇到「其他 offer 怎么选」,我的策略是给出决策维度(业务赛道、技术成长、团队匹配),而不是直接表态。
复盘与建议
字节后端面试通关密码(2025 版)
| 维度 | 权重 | 准备要点 |
|---|---|---|
| 算法 | 45% | 滑动窗口/双指针、图论、树模板三块高频,全部要会写模板 |
| 项目 | 30% | 量化指标 + 兜底方案,把实习项目当生产系统准备 |
| 系统设计 | 15% | 通知系统、弹幕/IM、Feed 流——可靠性、限流、去重必答 |
| 基础 | 10% | Java 集合源码、并发、MySQL 索引、Redis 原理 |
2025 算法趋势
- 滑动窗口/双指针取代部分老 DP 题成为一面主力(LC 3/76/209/239 高频出现)。
- 图论(拓扑、并查集)在三、四面出现频率上升,因为能顺带考察「数据建模」。
- 模板要能默写:面试官会直接让你「写个单调队列」,背不下来会很被动。
高频追问准备清单
- 你的接口峰值 QPS?压测怎么做的?瓶颈在哪?
- 缓存/Redis 挂了怎么兜底?数据怎么恢复?
- 频控用固定窗口还是滑动窗口?为什么?
- 消息可能重复消费吗?幂等怎么保证?
- QPS 翻 10 倍,哪里先崩?怎么扩?
- 为什么选这个数据结构/算法?trade-off 是什么?
与其他公司对比
| 公司 | 侧重 | 与字节对比 |
|---|---|---|
| 字节 | 算法 + 项目 + 系统设计并重 | — |
| 阿里 | 项目深度 + 架构思维 | 算法难度略降,更考业务建模 |
| 腾讯 | 基础 + 开放题 | 算法相近,项目追问深度略低 |
| 美团 | 工程实践 + 业务理解 | 场景题多,算法中等 |
祝你面试顺利!2025 字节更看重「能把算法模板落到项目里」的工程感——刷题之余,多想想每个模板在你的实习项目里怎么用,面试官会眼前一亮。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。