字节跳动 后端研发工程师一面面经:事件分发、幂等、一致性与 Redis
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
时长 1 小时。
- 自我介绍
- 详细介绍实习项目
- 讲一下事件分发,怎么实现的?
- 原理是什么?
- 讲一下 Kafka 的事件分发
- 你们的业务怎么处理高并发?
- 怎么保证幂等性?
- 怎么保证数据库一致性(加锁)?
- 什么锁?(乐观锁还是悲观锁)
- 讲一下乐观锁、悲观锁
- 你们用的什么锁?为什么?
- 什么是一致性?
- CAP
- 了解 Redis 吗?讲一下
- 为什么你们没用 Redis?
- Redis 加锁原理
- 数据库的隔离级别
- 锁与数据库隔离级别的关系
- 你们的数据库是什么级别?
- LangGraph 使用 Redis 存储 checkpoint 怎么实现的?
- Redis key 怎么设计的?
- 数据什么时候落库?
- QPS 有测过吗?
反问
算法:
- 手写 LRU
- LRU 为什么没有过期时间?
- 大量 key 过期,你的方案怎么处理?
《参考解析》
-
事件分发要从「一条事件怎么走到消费者」分层讲:先说事件怎么定义(事件类型、业务主键、幂等键、时间戳),再说总线怎么投递(Topic 与分区怎么划、按什么 key 选分区),最后说消费端怎么路由到具体处理器(按事件类型分发到不同 handler、线程池或消费者组并行)。讲 Kafka 时必须主动点出分区是有序性的边界:只有同 key 落到同分区才有局部有序,全局有序做不到,所以业务的顺序依赖要靠选 key 来保证,而不是笼统地说「Kafka 保证顺序」。同一个链路还要能说清失败怎么办——重试几次、退避策略、死信与人工兜底。
-
幂等性的答案要落到「幂等键 + 去重表」而不是口号:可行做法是用业务唯一标识(订单号、请求流水号 + 业务动作)当幂等键,写进带唯一索引的去重表,或者用 Redis 的 SET NX 抢位;插入冲突就视为重复请求,直接返回上一次的结果。状态机类操作再用乐观锁(version 字段或状态前置条件)保证「A→B」只能发生一次。顺手区分接口幂等和消息消费幂等:前者靠 token 或去重表,后者靠消费位点加业务去重,两者都不能只依赖 MQ 的 at-least-once 语义。
-
一致性、CAP 和锁要串成一条线答:一致性指多副本、多节点读到的数据符合业务预期,强一致、线性一致、最终一致的代价依次不同。CAP 说的是网络分区发生时只能在一致性与可用性之间取舍,工程上的答案是按场景分——交易主链路偏 CP,宁可写失败也不能写错;查询、推荐、统计偏 AP,读到稍旧的数据可以接受。锁的部分讲清乐观锁(版本号、CAS,适合冲突少的场景)与悲观锁(select for update、分布式锁,适合冲突多且必须串行的场景),并且给出自己系统选它的原因,而不是背定义。
-
隔离级别要答到「靠什么实现」:四级隔离级别分别解决脏读、不可重复读、幻读,实现手段是锁加 MVCC——InnoDB 默认可重复读,快照读靠 MVCC 拿一致性视图,写操作靠间隙锁、临键锁防幻读;读已提交下每次快照读都取最新已提交版本。被问「你们库是什么级别」要照实答,并说明理由:不少互联网业务把默认的 RR 调成 RC,原因是 RR 的间隙锁会放大锁冲突和死锁概率。
-
Redis 三连(为什么没用 / 加锁原理 / key 设计)是同一道题的不同切面:加锁原理讲 SET NX PX 原子加锁 + value 存唯一标识 + Lua 比对后再删,接着主动说坑——锁续期、业务耗时超过锁过期时间、主从切换丢锁、锁被别的线程误删。「为什么没用 Redis」这类反向问题考的是决策意识,常见理由是有状态数据量不大、一致性要求高、引入缓存会带来双写与失效问题、多一个组件就多一份运维成本;如果确实没引入,就照实讲当时的取舍,别硬编一个高大上的理由。
-
LRU 与「大量 key 过期」:手写 LRU 的标准解是哈希表加双向链表,get/put 都是 O(1),讲清为什么要哑头尾节点、并发场景下要加锁还是分片。面试官追问「怎么没有过期时间」,本质是在区分 LRU(按访问顺序淘汰,容量满了才淘汰)和 TTL(按写入时间过期),生产级缓存通常是惰性删除加定期抽样删除的组合:读时发现过期就删,后台按比例抽样清理大批过期 key,另外给过期时间加随机抖动、用互斥重建或逻辑过期兜住缓存击穿,避免大量 key 同时失效把压力全打到数据库。