最近训练营学员给我们分享了一篇很新的百度后端面经,题目还挺典型,尤其是 Go 并发、锁、调度模型、库存扣减、系统重构这些内容,问得很细,也很贴近真实项目。
这篇我就不搞太复杂,直接把面试里出现的重点题目和回答思路整理出来。准备后端面试的同学,特别是 Go 方向,建议认真过一遍,很多题不是只会概念就够了,面试官更想听你怎么结合项目去讲。
题目主要集中在这几个方向:
1. Go 的内存逃逸,面试官一般会怎么问?
这个问题很常见,很多人知道“逃逸到堆上”,但一展开就说不清。
可以怎么答
先说定义:
内存逃逸,本质上是编译器在编译阶段分析变量的生命周期。如果一个变量不能确定只在当前函数栈帧内使用,或者它的生命周期超过了函数调用本身,那它就可能不会分配在栈上,而是分配到堆上。
常见会发生逃逸的场景
第一,在函数里返回局部变量的指针。 因为函数结束后,栈帧本来要销毁,但这个变量还被外部拿着用,所以只能放到堆上。
第二,把指针或者带指针的值发送到 channel。 因为编译时没法确定到底哪个 goroutine 什么时候取走这个值,所以生命周期不好判断,容易逃逸。
第三,切片里存放指针或者带指针的对象。
比如 []*string 这种,切片里的引用对象通常会逃逸到堆上。
第四,slice 扩容。 append 之后如果超过容量,底层数组可能重新分配,新分配的那部分往往是在堆上。
第五,在 interface 类型上调用方法。 因为 interface 的真实实现要到运行时才知道,所以相关对象更容易发生逃逸。
面试加分说法
你可以补一句: “逃逸不等于坏事,Go 编译器这么做是为了保证正确性。真正要关注的是,逃逸过多会带来额外 GC 压力,所以性能敏感场景会尽量减少不必要的逃逸。”
这一句说出来,整体就会更像有实战经验的人。
2. GMP 调度模型,协程能不能无限创建?
这题很爱和项目里的高并发一起问。
先把 GMP 讲清楚
G 是 goroutine,P 是调度器里的处理器,M 是真正执行任务的线程。Go 调度的核心,就是 P 把 G 分配给 M 去跑。
面试里可以顺着说这几个点:
- P 有本地队列,优先从本地取 G 执行
- 本地没活了,会尝试从全局队列拿
- 再拿不到,就可能去别的 P 那里做 work stealing
- 如果 goroutine 因为系统调用或者 IO 阻塞,P 会尽量和 M 分离,避免影响其他 G 的调度
协程能不能无限创建?
不能。
理由可以从 3 个方面讲:
第一,内存有限。 每个 goroutine 初始栈虽然只有 2KB,但如果量很大,内存照样扛不住。
第二,调度有成本。 goroutine 太多,调度队列会积压,线程切换、任务切换都会带来开销。
第三,线程和系统资源也有限。 M 不是无限的,goroutine 一旦大量阻塞,也会拖垮整体调度效率。
面试建议
如果你项目里做过协程池、并发控制,这里一定要带一句: “理论上 goroutine 很轻量,但工程上仍然要控制并发,常见做法是 worker pool、信号量、限流或者任务队列。”
3. 并发量上来后用了锁,结果 panic 了,服务直接退出,怎么排查?
这题特别像真实线上问题,回答时一定别只说“看日志”。
一个比较完整的排查思路
第一步,先定位 panic 类型
先看日志和堆栈,确认到底是什么 panic。常见的有:
- nil 指针解引用
- 切片或数组越界
- map 并发读写
- 重复 close channel
- 类型断言失败
- 除零
- 资源关闭后继续调用
第二步,看是不是 OOM 或 goroutine 泄漏
如果并发量起来后内存暴涨,那要重点排查:
- goroutine 数量是否异常增长
- 是否有 goroutine 卡在 channel、锁、网络请求上
- 是否存在任务堆积导致资源迟迟不释放
第三步,检查锁的使用方式
重点看这几个问题:
- 锁粒度是不是太大
- 锁里有没有放慢操作,比如 DB、RPC、磁盘 IO
- 有没有嵌套锁或循环等待
- 有没有忘记释放锁
- 是不是有共享资源漏加锁,比如 map 只对写加锁、读没加锁
第四步,再看依赖资源是不是顶满了
比如:
- 数据库连接池满了
- 下游服务超时
- 某个依赖阻塞,导致大量 goroutine 堆积
这题怎么说更像实战派?
你可以补一句:
“如果是线上服务,我会先止损,比如 recover 兜底、限流、降级,再去定位根因。因为 panic 导致整个服务重启,影响的不只是当前请求,而是整体可用性。”
这样回答会更成熟。
4. 聊聊你对 mutex 的理解
这题如果只回答“互斥锁,保护共享资源”,基本不够。
可以这么展开
sync.Mutex 是 Go 里的互斥锁,用来保证同一时刻只有一个 goroutine 能进入临界区,主要是保护共享资源的并发安全。
你可以继续说的点
1)它简单直接
常用就是 Lock 和 Unlock,Go 1.18 之后也支持 TryLock。
2)底层效率还不错
底层依赖原子操作和信号量机制,轻竞争时开销低,重竞争时会进入等待和唤醒流程。
3)它不是可重入锁
同一个 goroutine 拿到锁以后,如果不释放,再次获取通常会出问题,所以设计时要避开重复加锁。
4)它有普通模式和饥饿模式
这一点很多人答不到,如果你提到,面试官一般会觉得你对底层有了解。
项目里怎么用?
可以举几个真实场景:
- 保护全局 map
- 做并发安全计数器
- 管理资源池,比如连接池、对象池
5. channel 怎么理解?有缓冲和无缓冲有什么区别?
这题本身不难,但很容易答得太空。
无缓冲 channel
无缓冲更像同步通信。发送和接收双方都要准备好,数据才能真正传过去。 所以它很适合做 goroutine 之间的同步配合。
有缓冲 channel
有缓冲更适合做异步解耦。 只要缓冲区还没满,发送就不会立刻阻塞;只要缓冲区里有数据,接收也不用等发送方在线。
面试回答建议
不要只停留在定义,可以顺带补一句:
“我一般会根据业务目标来选。如果是任务通知、控制执行顺序,偏向无缓冲;如果是生产消费、削峰填谷、异步处理,偏向有缓冲。”
6. 协程池怎么理解?平时怎么用?那进程池呢?
这题本质上考的是“你会不会控制并发”。
协程池的核心价值
协程池的作用主要有两个:
- 复用 goroutine,减少频繁创建和销毁的成本
- 控制并发数,避免 goroutine 数量失控
日常怎么用?
可以结合你做过的项目场景讲,比如:
- 批量发消息
- 批量调用第三方接口
- 批量处理任务队列
- 爬虫、导入导出、异步重试等场景
至于进程池
如果面试官追问,你可以这么答:
Go 里平时更常见的是 goroutine 池,进程池在 Go 后端业务里不是主流方案。因为多进程通信成本更高,资源也更重,除非是做隔离性很强的任务、调用外部程序,或者某些需要进程级隔离的场景。
7. 项目里做系统重构,一般会考虑哪些因素?
这是典型项目题,很多人容易讲成“代码不好维护所以重构”,太空了。
这份面经里给出的点很实用
重构的核心原因包括:
- 超时请求增多
- 单表数据量过大,开始往分库分表方向演进
- 业务耦合严重
- 模块边界不清晰
- 可维护性变差
- 技术债越来越多,后面迭代困难
另外还可以补充:
- 可扩展性不足,支撑不了新业务
- 技术栈升级,比如从自研框架切到 go-zero
- 团队和人力安排允许分阶段重构
这题怎么答更完整?
建议按“为什么重构—怎么拆—怎么平滑迁移—效果怎么样”这个顺序讲。
面试官其实最想知道的不是你知不知道重构,而是你有没有参与过有结果的重构。
8. 抽奖系统里,怎么保证奖品库存和下单前后的一致性?
这个题目非常典型,属于高并发系统设计常问题。
一个比较稳的回答方式
方案一:Redis 原子操作
可以用 Lua 脚本把“查库存 + 扣库存”封装成一个原子操作,这样能避免并发下超卖。
方案二:分布式锁 + 最终一致性
整体链路可以这样设计:
- 用 Redis 的
SETNX抢分布式锁 - 扣库存成功后发 MQ
- 订单服务异步消费消息创建订单
- 如果订单创建失败,走补偿任务回滚库存
面试时可以顺便提一句
“一致性不是只有强一致一种方案,高并发业务里很多时候会在性能和一致性之间做平衡,库存场景常见就是先扣减、再异步落单、失败补偿,走最终一致性。”
这句很加分。
9. 抽奖项目怎么防恶意请求和安全冲击?
这题现在问得越来越多,因为系统设计已经不只是“能跑”,还要“扛得住”。
可以从这几个方向回答
限流
用 go-zero 的限流能力,按 IP 或用户维度限制请求频率,比如一分钟最多抽 10 次。
鉴权和验证码
要求用户登录,校验 JWT 或 OAuth2 token;必要时加图形验证码或滑块验证码,防止脚本刷接口。
防重复提交
每个请求带唯一 request_id,Redis 记录已处理请求,短时间内重复请求直接拒绝。
黑名单机制
对异常高频、恶意行为的 IP 或用户做封禁。
参数校验
对用户 ID、奖品 ID 等关键参数做合法性校验,避免非法参数攻击。
回答重点
别只列技术点,最好强调一句:
“安全策略不是单点措施,而是限流、鉴权、幂等、校验、黑名单多层组合。”
10. 商品库存扣减,用 Redis 还是 MySQL?
这个问题很适合拉开差距,因为它考的是你有没有理解高并发场景下的取舍。
更推荐的回答
高并发场景下,一般优先 Redis 扣库存,MySQL 做最终落库和兜底。
一个完整流程可以这么讲
1)库存预热
系统启动或者活动开始前,把 MySQL 的库存同步到 Redis。
2)扣库存
用 Redis Lua 脚本原子扣减库存,成功后发 MQ,后续异步更新 MySQL。
3)兜底方案
如果 Redis 不可用,可以降级到 MySQL 乐观锁;同时通过定时任务校验 Redis 和 MySQL 数据一致性。
为什么不直接用 MySQL 扣?
因为:
- MySQL 是磁盘型存储,性能上限没那么高
- 高并发下乐观锁会大量重试
- 悲观锁又容易造成阻塞
这题如果你能把“性能 + 一致性 + 降级 + 补偿”都讲到,基本就比较稳了。
11. 业务拆分里,为什么把非必要同步流程改成异步?
这个问题其实很贴近线上优化。
面经里的例子很标准
优化前流程:
用户请求抽奖 → 扣库存 → 创建订单 → 发短信通知 → 返回结果 整个链路都串行,接口耗时高。
优化后流程:
用户请求抽奖 → 扣库存 → 先返回成功 后续订单创建、通知发送、积分更新、日志记录全都异步化。
这么做的好处
最直接的就是接口 RT 大幅下降,核心链路更短,吞吐更高,用户感知也更好。
面试答法建议
你可以顺手补充一句:
“不是所有流程都要异步,核心是区分主链路和非主链路。对用户结果无影响、允许延迟处理的步骤,更适合异步。”
12. 怎么衡量接口优化到底有没有效果?
这题很重要,因为它考的是“你优化完怎么证明自己真的优化了”。
核心指标一般看这些
响应时间
看平均 RT、P95、P99。 比如优化目标可以是 P99 从 500ms 降到 100ms 以内。
吞吐量
也就是 QPS/TPS,看单位时间处理能力提升了多少。
错误率
包括超时错误率、业务错误率。
资源利用率
CPU、内存、数据库连接池这些有没有更健康。
最好怎么说?
你可以加一句:
“优化不是只看接口变快,还要看高并发下系统稳不稳,所以我会结合压测和监控一起看,重点关注 P99、错误率和资源占用。”
这就比单纯说“看 QPS”强很多。
13. validate 库为什么能提升效率?怎么证明?
这个问题其实是在考你有没有做过工程化提效。
可以从 4 个角度说
1)减少重复代码
很多参数校验不需要再手写一堆 if-else,通过标签就能统一处理。
2)提升开发效率
开发接口时,直接写校验规则,省掉重复劳动。
3)性能可接受
高并发场景下不会成为明显瓶颈。
4)规则统一、提示统一
团队里参数规则一致,返回错误也更规范,前后端联调更顺。
那“依据”怎么讲?
这个特别关键,别只说感觉变好了。 面经里给出的说法是:
- 参数校验相关 bug 率明显下降
- 压测下校验部分 CPU 占用率下降
这类回答说明你不是“主观觉得提效”,而是拿指标说话。
14. 相似算法做文章推荐,结合布隆过滤器去重,完整流程怎么讲?
这题属于“算法 + 工程实现”结合,答得好很加分。
可以按业务链路来讲
1)数据预处理和特征提取
收集文章内容、用户点击阅读行为,用 TF-IDF、Word2Vec 或 BERT 提取文章特征。
2)召回候选文章
基于用户历史阅读文章,计算相似度,或者用协同过滤召回候选集。
3)用布隆过滤器做去重
为每个用户维护已读文章集合的布隆过滤器。 候选文章过一遍过滤器:
- 过滤器判定不存在,那就一定没读过
- 如果判定存在,可能是误判,再查数据库确认
4)排序
按相似度、热度、发布时间等综合排序,选出最终结果。
5)更新数据
用户读了新文章后,把文章 ID 加进布隆过滤器,同时落库更新阅读记录。
面试官为什么喜欢问这个?
因为它能同时看出你对推荐流程、去重思路、空间换时间设计、误判处理这些内容到底熟不熟。
最后说几句
看完这份面经,能明显感觉到一个趋势: 现在后端面试越来越不只是问八股,更多是在问你有没有把基础知识和项目真正连起来。
像 Go 的逃逸分析、GMP、mutex、channel,这些是基础。 但库存一致性、异步拆分、限流防刷、性能指标、系统重构,这些才是面试里真正拉开差距的部分。
如果你最近也在准备 Go 后端面试,我的建议就一句:
一定别只背概念。每一个知识点,最好都能落到“项目里我怎么用、为什么这么做、效果怎么样”这三个问题上。
这样答出来,面试官会更容易觉得你是能真正上手做事的人。
END
写在最后:
最近私信问我面试题的小伙伴实在太多了,一个个回有点回不过来。
我花了两个周末,把星球里大家公认最容易挂的 AI/Go/Java 面试坑点 整理成了一份 PDF 文档。里面不光有题,还有解题思路和避坑指南。
想要的同学,直接关注并私信我 【面试】,我统一发给大家。