ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

百度Golang后端日常实习三轮面试全复盘:从Go基础到系统设计

百度Golang后端日常实习三轮面试全复盘:从Go基础到系统设计 最近刚面完百度的 Golang 后端日常实习三轮技术面连着走下来整个过程比自己想象中扎实复盘的时候发现很多问题其实都是可以在准备阶段提前解决的。这篇文章就用自己的真实经历把三轮面试拆开讲讲面试官到底在问什么、我当时是怎么答的、以及后来回头看哪些地方可以答得更好。如果你也准备投 Go 后端实习尤其是日常实习方向这篇应该能帮你避开不少坑。1. 面试前我到底怎么准备的1.1 先搞清楚日常实习在招什么人很多人准备实习面试时最大的问题是拿校招真题在那里死磕结果花了很多时间背八股面试官一问项目细节就答不上来。日常实习和暑假那种正式实习不太一样它更看重的是你能不能快速上手干活代码基础扎不扎实遇到问题有没有自己的排查思路。我在投递之前没有急着刷题而是先理清楚一件事大厂的 Go 后端日常实习日常做的事基本就是写接口、做需求、修线上问题所以面试官会用大量时间验证“你是不是真的写过项目”而不是“你是不是背了一堆知识点”。这意味着你简历上写的每一个技术点都要经得起连续追问。1.2 我的准备清单和时间安排我给自己列了一张准备清单大致包含五块Go 基础与并发、数据库、Redis、计算机网络、算法题。时间分配上我比较偏向基础和项目大概是 Go 基础 25%、项目深挖 25%、数据库和网络 25%、算法 20%、场景设计 5%。为什么会这么分因为日常实习面试中算法题一般不会特别难考的是基本功相反项目深挖和原理题才是区分度最大的地方。我准备阶段最大的底牌是一个自己动手写的社区内容服务项目技术栈就是 Go MySQL Redis包含用户登录、内容发布、feed 流、评论点赞这些功能。这个项目几乎养活了我三轮面试后面每个方向的问题都能从项目里找到例子回答。2. 第一轮技术面Go 基础与项目初探2.1 从“你做过什么”开始的第一波拷问第一轮面试开头几乎不会有太多寒暄面试官直接让我讲项目。这里我强烈建议大家准备一个自我介绍的项目讲述模板结构就是“背景 - 方案 - 难点 - 结果”时间控制在三分钟以内。我当时讲的是社区内容服务里的 feed 流功能没有只讲“我做了个列表”而是讲清楚为什么用 Redis 的 zset 存时间线为什么用推拉结合而不是纯推或纯拉。结果就是这句话本身变成了导火索面试官顺着 zset 的底层结构一路问到底从跳表问到为什么不用红黑树。所以你在讲项目时每一句设计决策都要能往下接三个问题否则就不要写进简历。2.2 Go 语言必问题slice、map、defer 和 channel第一轮的基础题集中在 Go 语言自身。我自己被问到的包括slice 和 array 的区别、slice 扩容机制、map 为什么并发不安全、channel 发送和接收的阻塞条件、defer 的执行顺序、panic 和 recover 的底层关系。其中最容易翻车的是 slice 扩容。面试官让我口述扩容之后底层数组是否变化append 之后为什么有时两个 slice 不共享底层数据。这种问题光背结论没用我建议准备阶段自己写个小 demo把扩容前后的数组地址打印出来看一遍比背十遍都牢靠。map 并发安全问题也一样不要只说“会 panic”最好能说清楚当多个 goroutine 同时读写时runtime 会检测到并发写并触发 fatal error以及为什么加锁开销比 atomic 操作大。defer 这道题也很典型面试官会给一段代码让你说出函数返回值是什么。核心就是记住 defer 在函数返回前执行并且如果返回值有名字defer 里修改返回值会生效如果返回值是匿名defer 里修改局部变量不会影响返回结果。这种题平常不写代码真的会记错。2.3 并发编程现场写码goroutine 的生命周期管理第一轮有一道手写题场景很常见并发请求多个接口全部成功后聚合结果返回超过一定时间就返回错误。我现场用 errgroup 实现但面试官马上追问如果其中一个请求失败其他 goroutine 会怎么样会一直跑下去还是被取消这就是在考 goroutine 泄漏和 context 取消机制。我补了一段用 context.WithCancel 的写法顺便讲清楚 errgroup 内部就是通过 context 传播取消信号的所以主协程一旦拿到第一个错误其他协程会通过监听 context.Done 主动退出。func fetchAll(ctx context.Context, urls []string) ([]string, error) { ctx, cancel : context.WithCancel(ctx) defer cancel() results : make([]string, len(urls)) var wg sync.WaitGroup var mu sync.Mutex var firstErr error for i, u : range urls { wg.Add(1) go func(idx int, url string) { defer wg.Done() select { case -ctx.Done(): return default: } resp, err : http.Get(url) if err ! nil { mu.Lock() if firstErr nil { firstErr err cancel() } mu.Unlock() return } defer resp.Body.Close() mu.Lock() results[idx] resp.Status mu.Unlock() }(i, u) } wg.Wait() if firstErr ! nil { return nil, firstErr } return results, nil }这里给一个非常实用的建议以后写任何 goroutine 代码先问自己两个问题——这个 goroutine 在什么条件下退出如果父任务已经结束它还会不会继续占用资源只要把“退出机制”想清楚并发编程题基本不会被问倒。3. 第二轮技术面原理深挖与项目血拼3.1 数据库从 SQL 到事务和索引第二轮一上来就是 MySQL面试官先问 InnoDB 的索引结构为什么用 B 树而不是 B 树。这个问题我答得比较顺从磁盘 IO 次数、范围查询、叶子节点链表几个角度展开。但面试官不会停在八股接下来追加了最左前缀、回表、覆盖索引、索引下推。最让我意外的是现场让我解释一条慢 SQLselect * from t order by id limit 100000, 10问为什么分页越深越慢怎么优化。我给出的方案是延迟关联先查主键再回表select t.* from t join (select id from t order by id limit 100000, 10) tmp on t.id tmp.id这种题没有标准答案关键在于你能说出慢的原因MySQL 需要先扫描并丢弃前 100000 行再返回 10 行回表次数还很多。面试官其实更看重你排查问题的思路而不是背一个优化公式。3.2 Redis缓存三兄弟和持久化选型Redis 部分问得也很常规但追问很细。缓存穿透、击穿、雪崩分别怎么解决Redis 为什么快单线程为什么还能快RDB 和 AOF 怎么选。穿透这个问题我主动提到了布隆过滤器面试官果然顺势问布隆过滤器会不会误判能不能删除为什么这里如果你能说出“普通布隆过滤器不能删除因为一个 bit 可能被多个元素映射直接清零会影响其他元素如果要支持删除可以用计数布隆过滤器或用 Redis 的 string 做计数”面试官通常会比较满意。虽然生产环境不一定真用但这个回答能体现出你考虑问题更全面。持久化选型我也被问了面试官问重启之后 Redis 数据会丢吗。我把 RDB 和 AOF 的机制讲了一遍强调两种策略的组合使用以及 AOF 重写的触发条件。这块我建议不要只记结论要理解“RDB 是快照适合恢复快但可能丢数据AOF 是追加日志数据更安全但恢复慢”。3.3 计算机网络与 Go web 框架细节第二轮还问了网络基础TCP 三次握手能不能两次为什么会有 TIME_WAITHTTP/1.1 和 HTTP/2 多路复用有什么区别。这些是后端面试的基本盘答错基本没戏。更贴近 Go 的是 web 框架细节。面试官问了一个问题在 gin 里注册一个路由后一个请求是怎么从监听端口到达 handler 的这个问题把很多人问懵了因为平时都在写r.GET(/ping, handler)根本不知道底层发生了什么。我把 net/http 的 ServeMux、gin 的 Engine、RouterGroup、handler chain 关系捋了一遍还现场写了统计请求耗时的中间件并要求支持提前终止调用链。func costTime() gin.HandlerFunc { return func(c *gin.Context) { start : time.Now() c.Next() duration : time.Since(start) log.Printf(path: %s cost: %v, c.Request.URL.Path, duration) } }中间件的实现原理其实就是把 handler 串成一个链表c.Next()会执行后续 handler如果在c.Next()之前 return后续链就不会执行。把这些搞明白比背十个 gin API 有用得多。3.4 项目里的“事故”复盘第二轮的痛点环节是让我讲一个线上问题排查过程。我说了接口突然变慢先看监控发现 DB 慢查询展开 SQL 后发现字段没走索引最后定位到是历史数据导致区分度低。面试官接着追问如果加了索引还是慢你怎么继续查说实话这个问题我没准备到当时愣了一下。我给出的回答是先看是不是索引失效再看是不是数据量太大换一个更贴合查询条件的复合索引同时把查询条件里的函数操作去掉最后考虑做读写分离或加缓存。面试官没有说对错但至少认可了排查思路是完整的。这里真的建议项目里要准备一到两个真实问题自己编的细节在追问下很容易露馅。4. 第三轮技术面算法、场景与反问4.1 算法题的节奏与边界处理第三轮开始是算法题我抽到的是实现一个支持过期时间的 LRU 缓存。这题面试频率很高表面上是 LRU实际上要看你能不能处理各种边界。我的做法是用 map 双向链表实现这也符合真正的 LRU 要求。写之前先和面试官说清楚思路然后边写边讲写完主动说“我补几个测试用例验证一下”。这一点我觉得很加印象分因为大部分候选人写完就结束了只有少数人会主动测试边界条件。type CacheNode struct { key, value int expireAt time.Time prev, next *CacheNode } type LRUCache struct { capacity int cache map[int]*CacheNode head *CacheNode tail *CacheNode }面试官提醒我注意“过期检查和容量淘汰同时发生时”的优先级。我当时的处理是每次 Get 先检查该 key 是否过期如果过期就删除并尝试淘汰最久未使用的节点。整体思路不算难但要求你逻辑严密不能漏掉任何一个分支。4.2 场景设计题接口幂等与限流第三轮后半段有一个场景设计题题目非常经典设计一个下单接口怎么防止用户重复提交怎么防止商品超卖。我的回答分了三层每一层都对应一类问题。第一层是防重复提交前端按钮置灰加后端幂等 token第二层是接口幂等用唯一订单号加数据库唯一索引重复请求会插入失败第三层是超卖扣减库存时要保证原子性不能先查再扣。面试官继续追问Redis 扣库存和数据库扣库存有什么区别Lua 脚本怎么保证原子性。我给出了一个简洁的 Lua 扣减库存思路并说明为什么不用事务和乐观锁来做。这道题我建议每个人都整理出一个标准答案模板因为几乎所有后端面试都有机会碰到。不要只答一个点要把问题分层让面试官看到你有系统设计的基本意识。4.3 反问环节怎么问才不算扣分技术面问题问完后面试官让我反问。我问了三个问题实习期间主要负责的业务模块是什么团队目前的 Go 技术栈和微服务框架有没有一对一 mentor 带。不建议一上来就问薪资、转正率哪怕你真的很关心。一是第一轮或第二轮面官未必清楚二是会让面试官觉得你关注点偏了。反问其实也是你判断团队的机会比如面试官提到“我们这边代码 review 很严格”说明团队对代码质量要求高如果提到“目前后段服务在从别的语言迁到 Go”说明你进去后可能有重构和迁移的活能学到不少东西。5. 三轮面试后的复盘中我踩过的坑5.1 简历上写的东西必须能讲出三层深度我最大的坑是简历上写了“使用 Redis 缓存热点数据”面试官问“热点 key 是怎么统计的”我当场卡住了。后来我把项目中每个关键点都往下钻了三层功能是怎么实现的、底层依赖是什么、出了故障怎么排查。建议大家在写简历时每写一个技术词都问自己三个问题为什么用、怎么用的、如果挂了怎么办。比如你写“用了连接池”就要知道连接池的大小怎么配、空闲连接怎么回收、连接泄露怎么排查。答不上来的点一定要在投递之前解决不然面试现场一定会穿帮。5.2 八股背得再熟也需要代码落地第二轮的教训是我在口头回答“slice 为什么并发不安全”时很熟但让我手写一个并发安全的计数器还是会纠结用 atomic 还是 mutex。后来我总结了一个训练方法每个知识点都要写一个小 demo然后把线程池、连接池、工作池这些真实场景套进去。比如实现一个固定数量的 worker pool就会自然用到 channel、WaitGroup、context、atomic比背十篇面试题有用得多。面试官要的不是一个会背答案的人而是看到一个真正动手写过代码、能自己封装组件的人。这个区别在日常实习面试里尤其明显。5.3 每轮面试结束后马上写复盘三轮面试间隔不长第一轮问过的问题很可能在第二轮以变形的方式再次出现。我每轮结束后都会趁记忆新鲜把没答好的点记到备忘录里当天就查资料解决。我会整理成一个四列表格问题是什么、我当时怎么答、更好的回答是什么、涉及的知识点有哪些。这个习惯让我的后续轮次明显从容了很多。不要只看别人的面经自己的复盘才是真正的面经。因为只有你知道自己哪里弱也只有在面试这个场景下记忆才会这么深刻。5.4 心态和节奏控制不会的题也要开口三轮面下来我最大的感受是面试官有时候会故意制造沉默看你怎么处理。遇到不会的题千万不要闷头想也不要说“我不会”。可以复述问题说出你已知的条件再给一个最朴素的方案然后逐步优化。有一道题我完全没有思路就先说“如果是线上环境我会先查监控和日志看是哪个环节耗时异常”面试官反而点了点头。技术面问的不是完美答案而是你解决问题时的思路。你愿意开口、能展示思考过程就已经比沉默的候选人强很多了。最后再分享一个小技巧面试结束之后把自己当成面试官重新用“他为什么这样问”的角度过一遍所有问题比单纯背十篇面经有用得多。百度这套三轮技术面问的东西其实一点不难难的是你愿不愿意把每个点都往深处多走一步。我现在的体会是日常实习面试更像一次技术体检查出来的问题只要肯补就是这次面试最大的收获。
返回列表