百度后端开发岗面经 13:缓存分层、Feed 流与线程安全
- 轮次
- 多轮面试合集
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- MySQL 如何承载海量请求?
- 本地缓存和 Redis 分别适用于哪些场景,需要考虑哪些机器数量和成本因素?
- 大量相同 Query 请求如何使用缓存或 Singleflight 合并处理?
- 进程内缓存承载不下时,内存和磁盘分别适用于哪些场景?
- 给定一个有向图,如何判断图中是否存在环?
- 介绍项目和实习经历
- 什么是 MySQL 覆盖索引?
- MySQL 深分页问题如何解决?
- 大模型幻觉为什么会出现,如何使用 RAG 缓解?
- 是否了解 MCP?
- 场景题:高风险规则下发可能影响算法准确率,如何设计一套基于 RAG 的规则下发方案?
- 自我介绍
- 链表反转
- k 个一组链表反转
- 朋友圈 Feed 流是怎么做的?
- 游标分页怎么做?追问:为什么不用传统分页?
- 点赞系统怎么做?
- 点赞数据存在哪里?追问:为什么先存在 Redis?
- 通过 MQ 写 MySQL,怎么保证数据一致性?
- 端用户反馈“消息发不出去”,你怎么定位?
- 没有全局异常,只有一个用户出问题,怎么沿链路排查?
- 如果定位为网络抖动造成的数据丢失,怎么处理?
- 知识库是怎么构建、优化和测评的?
- 讲一下 Go 的垃圾回收原理
- 一个 1 MB 日志文件,怎么统计 Top-K IP?
- 用一条 Linux 命令实现呢?
- 如果日志超过 100G,怎么做?
- 自我介绍,重点讲实习期间产出
- Java 创建线程的方法有哪些?
- 线程池按照自身特点可以归纳为哪几大类?
- 线程安全的集合有哪些?
- 什么时候用 Map,什么时候用 List?
- 当前场景需要用 List 且要保证线程安全怎么办?
- synchronized、ReentrantLock 这些关键字和可重入锁,它们的区别是什么?
- 常见的排序算法有哪些?
- 快排的算法原理?
- 一个项目里如果有同(原帖在此截断)
《参考解析》
1. MySQL 承载海量请求的分层思路。数据库前面的每一层都是为了把请求挡在更便宜的地方:最前面是 CDN 与静态化,其次进程内缓存(Caffeine,纳秒级、无网络开销,但多实例间不一致且容量受堆限制),再是 Redis(共享、可扩容,代价是一次网络往返和序列化),最后才落到 MySQL。MySQL 自身要做的是把无效请求变少:覆盖索引避免回表、读写分离把分析类查询挪到从库、热点行拆分、大事务切小、连接池限流。成本上要算清楚的是命中率拐点——缓存容量继续加大但命中率不再上升时,多加的机器就是纯浪费。
2. Singleflight 合并相同请求。同一个 key 在极短时间内被大量并发请求时,只让第一个请求真正回源,其余请求挂在同一个结果上等待,这就是 Singleflight(Go 的 golang.org/x/sync/singleflight 是标准实现)。它和缓存的区别是:缓存解决「重复的读」,Singleflight 解决「同一瞬间的重复读」,两者叠在一起才能挡住缓存击穿。要注意的是它只对同一实例内的并发有效,多实例下仍需靠 Redis 分布式锁或逻辑过期来兜底。
3. 有向图判环。两种标准解法:DFS 三色标记,节点分未访问、访问中、已完成,遍历中遇到「访问中」的节点说明存在后向边即存在环;或者拓扑排序(Kahn 算法),不断把入度为 0 的点入队删除,若最终删掉的节点数少于总数则说明剩下的节点互相成环。DFS 更好写,拓扑排序顺便能得到一个合法的执行顺序——如果面试官追问「环上的节点是哪些」,拓扑排序剩下的那部分就是答案。
4. 深分页为什么慢,怎么改。LIMIT 1000000, 20 的执行方式是先取出前 1000020 行再丢掉前 1000000 行,翻得越深越慢,而且回表次数跟偏移量成正比。三种改法:基于游标(记住上一页最后一条的 id 或排序键,WHERE id > ? ORDER BY id LIMIT 20),走覆盖索引先只查主键再回表关联,或者业务上直接限制最大翻页深度。游标分页的代价是不能跳页,这也是追问「为什么不用传统分页」时要答的取舍。
5. 点赞系统与 MQ 写库一致性。点赞是先写 Redis 再异步落库的典型场景:Redis 里存业务维度的计数和「谁赞过谁」的集合,读路径完全不碰 MySQL,写路径把事件投到 MQ 再批量合并入库。之所以先存 Redis,是为了把高频写打在内存上、避免每点一次赞就打一条 UPDATE。一致性上要做到最终一致:MQ 至少一次投递 + 消费端幂等(用「用户 + 目标」的唯一键或 Redis set 的幂等性),入库存绝对计数或明细二选一;如果业务要求强一致,就得接受同步写库的性能代价,把取舍讲清楚比给一个万能答案更重要。
6. 1 MB 与 100G 日志统计 Top-K IP。1 MB 可以整份读进内存,awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20 一条管道就够;一次读入时用哈希表计数是 O(n),比排序的 O(n log n) 更快,但 1 MB 量级下差别可以忽略。100G 装不下内存,标准答案是 MapReduce 思想的分治:按 IP 哈希(比如 hash(ip) % 1000)切成 1000 个小文件,保证同一 IP 一定落在同一个文件里,然后逐个文件用哈希表统计出各自的 Top-K,最后对 1000 组 Top-K 做一次归并排序取全局前 K。如果 IP 分布集中在少数几个值上导致分片倾斜,可以切两轮或者换更细的哈希,面试官通常会追问这一点。
7. Go 的垃圾回收。Go 用的是并发三色标记清除,配合写屏障保证标记期间对象状态一致,GC 与用户协程并发执行,只在很短的阶段暂停(STW)。触发条件是内存增长达到 GOGC 设定的比例(默认 100%,即堆翻倍时触发)。因为不做分代也不做压缩,它的停顿是毫秒级以下,但代价是内存占用偏高、吞吐不如分代回收器。面试里可以补一句调优手段:GOGC 调大减少 GC 频率、GOMEMLIMIT 给堆设上限避免内存爆掉。