一场面试 11 连问,背八股很容易当场露馅

刚开始做 Go 微服务那会儿,我以为面试就两类题:写算法背八股
后来才发现,很多面试官真正喜欢的,是这种“连环追问”:

你会用就行?那你说说为什么这么用;你说它快?那你说说快在哪;你说系统稳定?那你说说极端情况下会发生什么

下面这篇面经不讲经历,只给你面试题 + 示例回答(并附带常见追问)。

如果你能把这些回答“讲顺”,大概率能过掉后端/微服务方向的核心拷问。

🪤 1)context 怎么用的?

面试官:context 你项目里怎么用?

很多人的“标准答案”是:context 就是用来传参的,或者加个超时。

但面试官真正想听的是:你是否理解它的边界传播链路

示例回答(面试版)

  • context 主要解决 3 件事:取消(cancel)超时/截止时间(deadline)跨边界的请求级元数据(value)
  • 我会把 ctx 作为函数的第一个参数,沿调用链往下传到所有可能阻塞的地方:DB/Redis/HTTP/gRPC/MQ publish 等。
  • 在入口(HTTP/gRPC handler)会拿到一个“请求根 ctx”,然后在内部需要更强约束的地方派生子 ctx:WithTimeout/WithCancel
  • WithValue 只放请求范围必须跨 API 边界的元数据(比如 traceId、userId),不把业务参数塞进去,更不会把 ctx 存到 struct 里长期持有。
func (s *Service) CreateOrder(ctx context.Context, req *CreateOrderReq) error {
    ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
    defer cancel()

    // 1) 传给 DB / RPC / Redis
    if err := s.repo.InsertOrder(ctx, req); err != nil {
        return err
    }

    // 2) 自己的 goroutine 也要“跟着 ctx 退出”
    go func() {
        select {
        case <-ctx.Done():
            return
        case <-time.After(200 * time.Millisecond):
            _ = s.metrics.Report(req.OrderID) // 示例:真正实现里也应传 ctx
        }
    }()

    return nil
}

常见追问(别踩坑)

  • context 里为什么不建议传业务参数?”——因为可读性差、无类型约束、容易滥用;更重要的是会让 ctx 的语义从“控制与元信息”变成“万能背包”。
  • “如果下游不支持 ctx 怎么办?”——包一层适配,或者保证取消时能主动关闭连接/停止消费,至少让 goroutine 能退出。

⚡ 2)为什么选 ES?ES 为什么快?

面试官:你们为什么用 Elasticsearch?它为什么快?

如果你只说“全文检索、分词”,基本等于没答。

示例回答(面试版)

  • 选 ES 是因为我们需要:全文检索 + 相关性排序 + 聚合分析 + 横向扩展,而这些在 MySQL 上靠 LIKE '%xx%' 或冗余字段很难做,且代价很高。
  • ES 快主要来自 Lucene 的索引结构与查询执行方式:

    • 倒排索引:从“词 -> 文档列表”,检索是集合运算,不是全表扫。
    • 段(segment)+ 不可变:写入先 refresh 成段,查询直接命中段文件,读路径更稳定。
    • 列式存储(doc values)与缓存:聚合/排序更高效;filter 走缓存、bitmap 等结构,速度明显。
    • 分片并行:一个 query 可以在多个 shard 上并行执行,再汇总结果。
  • 我们会把 ES 的角色定位成“检索与分析引擎”,主数据仍以 DB 为准,ES 通过增量同步/异步消费来更新,接受一定的最终一致

常见追问(面试官爱追)

  • “ES 写入也很快吗?”——写入是吞吐型,refresh/merge 有成本;高写入要控制 refresh interval、合理分片、避免频繁更新同一文档导致段合并压力。
  • “为什么不用 PG 的全文检索?”——可以用,但在相关性、生态、横向扩展、聚合性能上各有取舍;我们偏向 ES 的成熟度与运维经验(按你项目实际说)。

🔌 3)模块之间的通信怎么做的?

面试官:你们服务/模块之间怎么通信?

示例回答(面试版)

  • 同步链路用 gRPC:IDL 清晰、性能好、统一错误码与超时控制,配合拦截器做 logging/trace/metrics。
  • 异步链路用 MQ 事件通知:把“必须立即返回”的路径做短,把“最终一致即可”的动作下沉到异步(比如发券、写日志、触发索引更新)。
  • 关键点是把工程问题讲清楚:超时/重试/幂等/顺序性/重复消费/可观测性
同步(gRPC)适合:
  + 强依赖、必须拿到结果(比如查库存、查配置)
异步(MQ)适合:
  + 最终一致、允许延迟(比如通知、异步落库、索引更新)

常见追问

  • “既然用 MQ,怎么保证不丢?”——生产端 confirm、持久化;消费端手动 ack;失败重试 + 死信;业务幂等兜底。

🐇 4)为什么选 RabbitMQ?

面试官:你们为什么是 RabbitMQ,不是 Kafka?

示例回答(面试版)(按你项目取舍讲):

  • 我们更看重低延迟、灵活路由、可靠投递、易运维

    • Exchange(direct/topic/fanout)路由能力强,适合业务事件分发。
    • 支持 ack、重回队列、死信队列、延迟队列(常见实现)等,做“业务兜底”很方便。
    • 对“任务队列/事件通知”这种模式很顺手。
  • Kafka 更偏“高吞吐日志流”,我们这类“业务事件 + 路由 + 消费确认”场景更贴 RabbitMQ 的模型。

常见追问

  • “RabbitMQ 性能不如 Kafka 你怎么看?”——是的,吞吐上 Kafka 更强,但选型要看指标:我们更在意路由与投递语义;并且我们当前量级 RabbitMQ 足够,后续量级变化再做架构演进。

🆚 5)RabbitMQ 和 Kafka 的主要区别?

这题建议你直接给“对比维度”,面试官听得最舒服。

示例回答(面试版)

维度RabbitMQKafka
模型队列/交换机路由分区日志(log)
消费以投递/确认为核心以 offset/重放为核心
历史消息通常消费即删除(按队列语义)按保留策略长期保存,可重放
吞吐中高(偏低延迟业务)很高(大吞吐流式)
顺序性单队列可保证分区内有序
典型场景业务事件、任务队列、复杂路由埋点日志、流式计算、CDC、事件总线

补一句更像“项目经验”的话:

我们用 RabbitMQ 做“业务事件通知”,用 Kafka(如果有)做“日志/埋点/CDC 流”,这样职责更清晰。

🌳 6)B+ 树的本质是什么?

面试官:B+ 树你怎么理解?“本质”是什么?

示例回答(面试版)

  • B+ 树的本质是:为磁盘/页存储设计的多路平衡搜索树,目标是用更大的扇出(fan-out)降低树高,从而减少 I/O 次数。
  • 它把数据(或主键)集中在叶子节点,内部节点只存索引键;叶子节点通过链表相连,因此:

    • 等值查询:从根到叶,I/O 次数可控;
    • 范围查询:定位到起点叶子后顺序扫,顺序 I/O 更友好。
  • 这也是为什么像 InnoDB 这类存储引擎会基于 B+ 树做索引:既能查得快,又能把范围与排序做得更自然。

常见追问

  • “B 树和 B+ 树差在哪?”——B 树数据可在内部节点;B+ 树数据都在叶子,范围扫描更稳。

📮 7)channel 了解多少?

面试官:Go 的 channel 你了解多少?有缓冲/无缓冲差别?

示例回答(面试版)

  • chan 是 Go 的并发通信原语,本质是“同步/队列化的通信通道”,用来在 goroutine 之间安全传递数据。
  • 无缓冲 channel:发送与接收必须同时准备好,天然同步(更像 rendezvous)。
  • 有缓冲 channel:缓冲没满时发送不阻塞;缓冲为空时接收阻塞;常用于削峰或做 worker pool。
  • 关闭语义:

    • 关闭后仍可接收(收到零值 + ok=false),但再发送会 panic;
    • 通常由发送方关闭,接收方不要随便 close(除非你能保证只有你在发送)。
select {
case v := <-ch:
    _ = v
case <-ctx.Done():
    return ctx.Err()
}

常见追问(高频坑)

  • nil channel:收发都会永久阻塞,常用于在 select 里动态开关分支。
  • “怎么避免 goroutine 因 channel 卡住?”——配合 context、超时、或明确关闭通道;不要让 for { <-ch } 在没有退出条件的情况下跑。

⚙️ 8)GMP 模型:如果阻塞了会怎么样?

面试官:讲讲 GMP,如果 goroutine 阻塞了会怎样?

示例回答(面试版)

  • G = goroutine,M = OS thread,P = 调度器的逻辑处理器(持有本地队列与运行时资源)。
  • 正常情况下:P 把可运行的 G 放到本地队列,绑定到某个 M 上执行。
  • 阻塞分两类(这是加分点):

    • 网络 I/O:Go 有 netpoll,通常会把 G 挂起(park),让 M 继续拿 P 跑别的 G,不会“卡死整个线程”。
    • 系统调用/长时间阻塞:M 可能被内核阻塞,运行时会把 P 从这个 M 上“摘”下来,交给别的 M 继续跑,必要时会创建新的 M 顶上。
  • 如果阻塞点没有退出条件(比如一直等 channel/锁、ctx 不取消),就可能出现协程泄漏:G 一直挂着,资源慢慢被吃光。

常见追问

  • “Go 不是有抢占吗?”——是的,新版本支持异步抢占,但它解决的是“长时间计算不让出 CPU”的问题;对“等待某个永远不会发生的事件”无能为力,所以还是要设计退出机制。

🧩 9)为什么要有 P?

面试官:有了 G 和 M,为什么还要 P?

示例回答(面试版)

  • P 的作用是把调度从“全局抢锁”变成“本地队列优先”,降低竞争:每个 P 有自己的 run queue,大多数调度都在本地发生。
  • P 也承担了运行时的一些资源绑定(比如某些缓存/状态),并通过 GOMAXPROCS 控制并行度:最多同时有多少个 P 在跑,也就最多同时跑多少个 goroutine(严格说是同时执行的 G 的数量上限)。
  • 没有 P 的话,所有 M 都去抢全局队列锁,调度开销会显著上升,尤其在高并发下。

🕳️ 10)什么情况下会协程泄漏?

面试官:协程泄漏你遇到过吗?一般怎么发生?

示例回答(面试版)

  • channel 永久阻塞:发送方没人接、接收方没人发;或者 for range ch 等不到 close。
  • 忘记取消/超时:内部起 goroutine 做重试/轮询,但 ctx 用了 context.Background() 或者忘了 cancel()
  • ticker/timer 没停time.NewTicker 没有 Stop(),goroutine 一直被唤醒做无意义工作。
  • 资源未关闭导致阻塞:网络连接、文件句柄不关,读写 goroutine 卡在 I/O 上。
  • fan-out 没收敛:每个请求开 N 个 goroutine,缺少并发上限/缺少 errgroup 统一回收。
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error { return s.callA(ctx) })
g.Go(func() error { return s.callB(ctx) })
return g.Wait()

面试官喜欢听到的“工程化收尾”:

我会给所有后台 goroutine 一个明确的退出条件(ctx / close channel / done),并且在压测和线上用 pprof 看 goroutine 数是否稳定。

🐳 11)讲一下 Docker 和 K8s

面试官:Docker 和 K8s 你怎么理解?区别是什么?

示例回答(面试版)

  • Docker 解决的是“怎么把应用和依赖打包并一致运行”:镜像(image)是交付物,容器(container)是运行态。
  • K8s 解决的是“一堆容器怎么在集群里自动化运维”:调度、扩缩容、滚动升级、自愈、服务发现、配置管理。
  • 常用对象我能讲清楚:

    • Deployment:无状态应用的发布与滚动升级
    • Service:稳定访问入口(负载均衡/服务发现)
    • Ingress:HTTP 路由入口(配合 Ingress Controller)
    • ConfigMap/Secret:配置与密钥
    • HPA:按指标自动扩缩容
    • StatefulSet:有状态服务(按需)

一句话总结(很好用):

Docker 更像“把程序装进标准集装箱”,K8s 更像“港口调度系统”,负责把集装箱放到合适的船上、坏了自动换、忙了自动加船。

✅ 总结:面试官其实在考什么?

当这些题连着问的时候,面试官通常不是要你背概念,而是看你有没有三种能力:

  • 能否把“会用”讲成“为什么这么用”(context、channel)
  • 能否把“选型”讲成“约束 + 取舍”(ES、RabbitMQ vs Kafka)
  • 能否把“并发”讲成“极端情况 + 退出机制”(GMP、协程泄漏)
你不需要把每个点都讲到论文级别,但你需要在关键处“说出底层原因”,并且能落到工程实践的兜底方案上。

END

写在最后:

最近私信问我面试题的小伙伴实在太多了,一个个回有点回不过来。

我大家公认最容易挂的 AI/Go/Java 面试坑点 整理成了一份 PDF 文档。里面不光有题,还有解题思路和避坑指南。

想要的同学,直接加我微信wangzhongyang1993,或者关注并私信我 【面试】,我统一发给大家。

标签: none

添加新评论