字节跳动暑期实习后端开发二面凉经
面试题目
一面(3/26)
项目与实习相关
- 实习相关提问(面试官倾听为主,给出设计建议)
- 用消息队列、Redis 做什么?
中间件 / 消息队列
- Kafka 的幂等性如何实现?
- 消息已经写入后,消费者如何确保只消费一次?
数据结构
- 数组和链表有什么区别?
- 如何判断一个链表有没有环?(讲思路即可,两种方法)
数据库
- MySQL 事务的 ACID,具体都是干什么的?
算法手撕
- 用 rand10 实现 rand7
- 合并两个排序好的链表(能用递归吗?)
- 合并 K 个有序链表
二面(3/30)
项目与实习相关
- 讲一下实习经历,挑一段自认为做得最好的经历详细说明
数据结构
- 了解哪些数据结构?
- 讲一下红黑树和 B+ 树
- MySQL 用的是什么数据结构?为什么不用 B 树?
数据库
- 事务 ACID(再次考察)
场景设计题
- 如何设计一个给账户充值或扣款的接口?请考虑得详细一些(涉及幂等性设计)
- 针对上述场景,幂等性如何保证?(连续追问)
AI 相关
- 有用过 AI Coding 吗?在什么场景下使用?使用时应注意什么?
算法手撕
- 判断一个链表有没有环(与一面重复)
参考解析
Kafka 幂等性实现
- Producer 幂等性:通过
enable.idempotence=true开启,Broker 为每个 Producer 分配 PID,结合 SequenceNumber 对每条消息去重,保证 单分区单会话 内 Exactly Once。 - Consumer 侧幂等:Kafka 本身不保证消费幂等,需业务层处理。常见方案:消息携带唯一业务 ID,消费前查询 Redis/DB 是否已处理,处理后写入去重标记,利用数据库唯一索引或分布式锁保证。
链表判环(两种方法)
- 快慢指针(Floyd 判圈):slow 每次走 1 步,fast 每次走 2 步,若存在环则必相遇。时间 O(n),空间 O(1)。
- 哈希表法:遍历链表,将每个节点地址存入 HashSet,若发现重复则有环。时间 O(n),空间 O(n)。
MySQL 为什么用 B+ 树而不用 B 树
- B+ 树非叶节点不存数据,仅存索引键,同样页大小能存更多键,树更矮,磁盘 IO 更少。
- 所有数据都在叶节点,且叶节点用链表串联,范围查询只需遍历叶节点链表,效率远高于 B 树的中序遍历。
- B 树每个节点都存数据,范围查询需要回溯,不适合数据库的顺序访问场景。
事务 ACID
- A(原子性):事务内操作要么全部成功,要么全部回滚,由 Undo Log 保证。
- C(一致性):事务前后数据满足业务约束(如转账总额不变),是 AID 共同保障的目标。
- I(隔离性):并发事务互不干扰,由 MVCC + 锁机制实现,分 4 个隔离级别。
- D(持久性):事务提交后数据永久保存,由 Redo Log(WAL 机制)保证。
充值/扣款接口幂等性设计
- 唯一订单号(幂等 Token):客户端生成全局唯一流水号,服务端以此为幂等键,写入前先查是否已处理。
- 数据库唯一索引:在流水表上对
(account_id, order_no)建唯一索引,重复提交直接报唯一键冲突,业务层捕获后返回成功。 - Redis 分布式锁 + 状态机:对订单加锁,同时维护订单状态(待处理/处理中/已完成),重复请求命中”已完成”直接返回。
- 扣款需加乐观锁:
UPDATE account SET balance = balance - ? WHERE id = ? AND balance >= ?,配合版本号防超扣与并发冲突。
rand10 实现 rand7(拒绝采样)
- 调用两次
rand10,构造(rand10()-1)*10 + rand10(),得到 1~100 均匀分布的值。 - 取前 70 个(1~70),对 7 取模加 1,即
val % 7 + 1,其余超出范围则重试(拒绝采样)。 - 也可简化:
(rand10()-1)*10 + rand10()取 1~70 范围,% 7 + 1 即可,期望调用次数约 2.86 次。
合并 K 个有序链表
- 最优方案:使用最小堆(优先队列),初始将 K 个链表头节点入堆,每次弹出最小节点接入结果链表,再将其 next 入堆。时间复杂度 O(N log K),N 为总节点数。
- 也可分治两两合并,时间同为 O(N log K),空间 O(log K) 递归栈。