面灵AI→

字节跳动 后端研发工程师一面面经:事件分发、幂等、一致性与 Redis

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

时长 1 小时。

  1. 自我介绍
  2. 详细介绍实习项目
  3. 讲一下事件分发,怎么实现的?
  4. 原理是什么?
  5. 讲一下 Kafka 的事件分发
  6. 你们的业务怎么处理高并发?
  7. 怎么保证幂等性?
  8. 怎么保证数据库一致性(加锁)?
  9. 什么锁?(乐观锁还是悲观锁)
  10. 讲一下乐观锁、悲观锁
  11. 你们用的什么锁?为什么?
  12. 什么是一致性?
  13. CAP
  14. 了解 Redis 吗?讲一下
  15. 为什么你们没用 Redis?
  16. Redis 加锁原理
  17. 数据库的隔离级别
  18. 锁与数据库隔离级别的关系
  19. 你们的数据库是什么级别?
  20. LangGraph 使用 Redis 存储 checkpoint 怎么实现的?
  21. Redis key 怎么设计的?
  22. 数据什么时候落库?
  23. QPS 有测过吗?

反问

算法:

  1. 手写 LRU
  2. LRU 为什么没有过期时间?
  3. 大量 key 过期,你的方案怎么处理?

《参考解析》

  1. 事件分发要从「一条事件怎么走到消费者」分层讲:先说事件怎么定义(事件类型、业务主键、幂等键、时间戳),再说总线怎么投递(Topic 与分区怎么划、按什么 key 选分区),最后说消费端怎么路由到具体处理器(按事件类型分发到不同 handler、线程池或消费者组并行)。讲 Kafka 时必须主动点出分区是有序性的边界:只有同 key 落到同分区才有局部有序,全局有序做不到,所以业务的顺序依赖要靠选 key 来保证,而不是笼统地说「Kafka 保证顺序」。同一个链路还要能说清失败怎么办——重试几次、退避策略、死信与人工兜底。

  2. 幂等性的答案要落到「幂等键 + 去重表」而不是口号:可行做法是用业务唯一标识(订单号、请求流水号 + 业务动作)当幂等键,写进带唯一索引的去重表,或者用 Redis 的 SET NX 抢位;插入冲突就视为重复请求,直接返回上一次的结果。状态机类操作再用乐观锁(version 字段或状态前置条件)保证「A→B」只能发生一次。顺手区分接口幂等和消息消费幂等:前者靠 token 或去重表,后者靠消费位点加业务去重,两者都不能只依赖 MQ 的 at-least-once 语义。

  3. 一致性、CAP 和锁要串成一条线答:一致性指多副本、多节点读到的数据符合业务预期,强一致、线性一致、最终一致的代价依次不同。CAP 说的是网络分区发生时只能在一致性与可用性之间取舍,工程上的答案是按场景分——交易主链路偏 CP,宁可写失败也不能写错;查询、推荐、统计偏 AP,读到稍旧的数据可以接受。锁的部分讲清乐观锁(版本号、CAS,适合冲突少的场景)与悲观锁(select for update、分布式锁,适合冲突多且必须串行的场景),并且给出自己系统选它的原因,而不是背定义。

  4. 隔离级别要答到「靠什么实现」:四级隔离级别分别解决脏读、不可重复读、幻读,实现手段是锁加 MVCC——InnoDB 默认可重复读,快照读靠 MVCC 拿一致性视图,写操作靠间隙锁、临键锁防幻读;读已提交下每次快照读都取最新已提交版本。被问「你们库是什么级别」要照实答,并说明理由:不少互联网业务把默认的 RR 调成 RC,原因是 RR 的间隙锁会放大锁冲突和死锁概率。

  5. Redis 三连(为什么没用 / 加锁原理 / key 设计)是同一道题的不同切面:加锁原理讲 SET NX PX 原子加锁 + value 存唯一标识 + Lua 比对后再删,接着主动说坑——锁续期、业务耗时超过锁过期时间、主从切换丢锁、锁被别的线程误删。「为什么没用 Redis」这类反向问题考的是决策意识,常见理由是有状态数据量不大、一致性要求高、引入缓存会带来双写与失效问题、多一个组件就多一份运维成本;如果确实没引入,就照实讲当时的取舍,别硬编一个高大上的理由。

  6. LRU 与「大量 key 过期」:手写 LRU 的标准解是哈希表加双向链表,get/put 都是 O(1),讲清为什么要哑头尾节点、并发场景下要加锁还是分片。面试官追问「怎么没有过期时间」,本质是在区分 LRU(按访问顺序淘汰,容量满了才淘汰)和 TTL(按写入时间过期),生产级缓存通常是惰性删除加定期抽样删除的组合:读时发现过期就删,后台按比例抽样清理大批过期 key,另外给过期时间加随机抖动、用互斥重建或逻辑过期兜住缓存击穿,避免大量 key 同时失效把压力全打到数据库。