面灵AI→

字节跳动后端一面面经(含 RedLock 与手撕)

轮次
一面
结果
已挂
时间
2026-10
来源
牛客网

《面试题目》

  1. 关于 agent 的场景题深挖。
  2. 看你做了优化相关的事情,说说你线程池这块怎么优化的?配置怎么根据具体情况设定?你在考虑优化的时候是直接上手吗?你会看线程池的哪些指标?
  3. 说一下你们的链路,怎么保证的一致性?
  4. 你平常分布式锁怎么用?如果 Redis 从切主导致锁丢失了怎么办?用 RedLock 就没问题吗?它有什么问题,有什么好的解决方案?
  5. 其他八股:你们有没有布置异地多活、数据库用过哪些等。
  6. 手撕:给定一个数组 A 和一个数 n,求能用 A 中数组成的不大于 n 的最大数。

《参考解析》

线程池怎么优化、看什么指标。第一步永远是测量而不是直接改代码:先看线程池的活跃线程数、队列长度、任务等待时间、任务执行耗时分布、拒绝次数、以及线程创建与销毁的频率(Java 里可以看 ThreadPoolExecutor 的 getActiveCount/getQueue().size()/getCompletedTaskCount,或接入 Micrometer 埋点)。拿到这些指标才能判断瓶颈在哪:队列一直满且活跃线程打满 → 容量不足或下游慢;活跃线程数远低于核心数但延迟高 → 卡在下游 I/O 或锁;频繁创建销毁 → 核心线程数设置不当或用了 allowCoreThreadTimeOut。参数怎么定,要按任务类型分开:CPU 密集型任务线程数接近核数(N+1),I/O 密集型可以更多(N × (1 + 等待时间/计算时间)),但更稳的做法是「按下游的承载能力和实测吞吐定」,并通过压测找拐点,别迷信公式。队列要选有界队列(ArrayBlockingQueue)并配好拒绝策略(CallerRunsPolicy 做背压、或自定义策略降级 + 告警),否则无界队列会把内存撑爆而不是暴露问题。另外要区分「优化线程池」和「优化被它调用的东西」——绝大多数线程池问题根因在下游慢,扩线程只是把压力转移。

链路一致性怎么讲。按「一致性在哪个环节被破坏」来组织:本地事务(数据库 ACID 保证单库操作原子),跨服务调用(用本地消息表 / 事务消息 / TCC / Saga 做最终一致,配幂等与重试),缓存与数据库(Cache Aside + 延迟双删 + binlog 订阅),消息投递(生产者确认 + 消费幂等 + 死信队列)。要能说出每种方案的代价:本地消息表实现简单但有时延、TCC 需要每个参与方实现 try/confirm/cancel 三个接口且要做好悬挂与空回滚、Saga 靠补偿且补偿必须幂等。答题时先给出业务允许的一致性级别(强一致还是最终一致),再选方案——面试官想听的是「你会不会先问需求」,而不是背方案名。

RedLock 与锁丢失。先说清单机 Redis 锁为什么可能丢:SET key value NX PX ttl 加锁、DEL(或 Lua 校验 value 后删)解锁,但如果主从异步复制期间主节点宕机、从节点升主,锁就没了(数据还没同步过去),于是两个客户端同时持锁。要缓解单点风险,常见做法是 RedLock:向 N(通常 5)个独立 Redis 节点请求加锁,多数派成功且总耗时小于锁有效期才算成功,释放时向所有节点发删除。RedLock 的问题(这正是面试官想听的):它依赖各节点的时间假设(时钟漂移会破坏有效期判断)、依赖各节点之间没有共享的状态、且在有 GC 停顿或网络延迟时难以保证正确性;分布式系统领域的经典批评(Martin Kleppmann 那篇)指出它不是一个有严格证明的共识算法。更可靠的替代路径:一是降低对锁正确性的依赖,把关键路径设计成幂等或乐观并发(数据库唯一索引、条件更新 WHERE stock >= n、版本号 CAS),这样即使锁失效也不会产生错误结果;二是用有共识保证的组件做协调,比如 ZooKeeper(临时顺序节点,会话断开自动释放)、etcd(租约 + revision)、或数据库行锁(SELECT ... FOR UPDATE);三是用 fencing token(单调递增的令牌),让下游能拒绝过期持有者的写入。答题顺序建议是「先说业务是否真的需要分布式锁 → 再说锁失效的兜底 → 最后才讨论 RedLock 的取舍」。

手撕:用数组 A 中的数组成不大于 n 的最大数。题意要先用例子确认(A 里的数可以重复使用吗?n 是数字还是长度?需要精确等于某位数还是位数不限?)。典型解法是贪心 + 回溯(DFS):把 A 排序去重,从高位往低位构造,枚举当前位能放的候选数字(不超过 n 对应位,且整体前缀不能已经超过 n),并携带「是否已经贴上界」的状态;如果选了小于上界的数字,后面所有位都可以填 A 中最大的数字;同时处理「不能有前导零」和「位数少于 n 时」的规则。搜索时用一个「当前位放哪个数字」的循环并排序成从大到小优先,找到第一个可行解即最优(因为是按位贪心)。时间复杂度与回溯剪枝分析要讲清:每层最多 10 个候选,最坏 O(10^L),但「一旦脱离上界就填最大值」这个剪枝让实际是 O(L × 10)。边界要覆盖:n 小于 A 中最小可用数字(可能无解,按题目约定返回 -1 或空)、A 中有 0(能否作为非首位)、n 有重复数字。面试里更重要的是边说边写、写完主动跑几个用例,这比一次写对更能拿分。

准备这类面试的着力点。字节这类面试的淘汰点往往不在八股广度,而在「往下追两层」。把简历上每一个「优化」「一致性」「组件选型」都准备好三层追问:现象与指标是什么、为什么这么选、失效场景与兜底是什么。作者也提到投递机会有限(字节限制两次投递),预约面试时间要跟准备进度匹配。