编辑
2024-06-02
随意记录
00
请注意,本文编写于 889 天前,最后修改于 0 天前,其中某些信息可能已经过时。

前置重要原则(防穿帮核心,一定要记住)

你的定位:业务Go后端开发,不是中间件专家、不是K8s运维

✅ 你能做:业务接入、参数配置、排查业务侧问题、处理使用中遇到的业务故障、写业务代码对接SDK

❌ 你不负责:集群搭建、内核调优、源码改造、运维层面的高可用架构搭建

万能兜底句(遇到超纲问题,语气平稳说,不要慌):

“这个组件的集群搭建、底层内核调优这块我们团队是运维同学负责维护,我日常主要是业务侧接入使用,解决业务层面的问题,底层原理有看过文档,还在持续学习。”

下面分四块,每一块:是什么 → 项目里你怎么用(贴合简历,这是重中之重)→ 高频面试题+标准回答(分基础版+进阶版,进阶答不上就切兜底句)→ 踩坑故事(非常关键!面试官最爱听,显得你真的用过)


一、Redis(最高频,优先背)

1. Redis是什么

Redis是基于内存的Key-Value非关系型数据库,支持多种数据结构(String、Hash、List、Set、ZSet),读写速度很高;同时支持RDB、AOF两种持久化,把内存数据落地磁盘,防止宕机全部丢失。

MySQL是磁盘数据库,主打持久化存业务主数据;Redis是内存库,不适合作为核心业务唯一数据源,一般做缓存、辅助能力。

2. 你在Go项目里怎么用(一定要背,贴合简历)

项目中使用go-redis这个SDK对接Redis,主要4个场景:

  1. 热点数据缓存:用户信息、配置信息、商品基础数据,先查Redis,命中直接返回;没命中再查MySQL,查到之后回写Redis,减少MySQL压力;

  2. 分布式锁:避免并发重复操作,比如防止重复下单、重复创建工单;用SET key value NX EX的方式(NX=不存在才设置,EX=自动过期);

  3. 计数器:文章阅读量、接口访问次数;

  4. 限流:接口维度的IP限流,防止恶意刷接口。

注意:你不要说用Redis做消息队列,你简历写了RabbitMQ,消息队列统一交给RabbitMQ,Redis只做简单的临时队列,生产异步任务优先用MQ。

3. 高频面试题 + 回答(带踩坑故事,防穿帮)

(1)缓存穿透、击穿、雪崩(必问)

  • 穿透:查询一个根本不存在的数据,Redis里一直没有,请求直接打到MySQL。

    回答:我们方案是缓存空值+接口层参数校验;如果是高并发场景可以布隆过滤器,但是我们当前业务量不大,用缓存空值就够用。

  • 击穿:某一个热点key过期,大量并发请求同时过来,同时打向MySQL。

    回答:两种方案,一是给热点key设置永不过期,后台异步更新;二是用分布式锁,只有一个请求去更新缓存,其他请求等待重试。我们项目用的是永不过期+后台定时刷新。

  • 雪崩:大量key集中在同一个时间过期,大量请求同时压垮数据库。

    回答:给过期时间加上随机偏移量,比如原本30分钟过期,改成30min + 0~300s随机值;同时做好服务降级,缓存失效的时候保护数据库。

(2)RDB和AOF区别

回答:RDB是定时生成快照,把整个Redis内存数据存到磁盘,文件小、恢复快,缺点是两次快照之间宕机,这段时间数据会丢;AOF是记录每一条写命令,追加写入文件,丢数据更少,但是文件体积大、恢复慢。生产环境我们一般是RDB+AOF同时开启,AOF作为主持久化,RDB用来快速恢复。

(3)缓存和数据库双写一致性问题(非常高频)

回答:我们采用「先更新MySQL,再删除缓存」的方案。

坑:一开始想过先删缓存再更新DB,并发场景下会出现:A删缓存 → B读到旧数据写入缓存 → A更新DB,最后缓存是旧数据,不一致。

补充:如果对一致性要求极高,我们会用延迟双删;大部分业务是最终一致性,不需要强一致。

(4)你踩过什么Redis的坑?(讲故事,加分)

背诵版本:之前踩过一个分布式锁的坑,锁过期时间设置太短,业务逻辑还没执行完,锁自动释放了,别的请求拿到锁,导致并发问题。后面改成:锁的过期时间大于业务最大执行时长,同时业务里尽量缩短锁内逻辑;后面也了解了红锁,但是我们业务简单,没有上红锁。

(5)进阶问题(比如Redis集群、哨兵)

如果问:Redis集群原理?哨兵怎么主从切换?

👉 直接兜底:我们线上Redis集群是运维统一维护,我业务侧只做SDK接入,集群高可用、主从切换的运维细节我看过文档,没有实操搭建经验。


二、RabbitMQ 消息队列

1. RabbitMQ是什么

RabbitMQ是消息中间件,核心作用:异步解耦、削峰填谷。

简单理解:生产者不用等下游所有逻辑跑完,把消息丢给MQ,立刻返回给前端;消费者异步慢慢消费消息。

核心组件:

  • Producer生产者:发消息的服务(你的Go后端)

  • Consumer消费者:消费消息的服务

  • Queue队列:存放消息

  • Exchange交换机:接收生产者消息,根据路由规则把消息转发到队列;4种常用类型:

    • direct:精准匹配路由键

    • fanout:广播,发给所有绑定队列

    • topic:模糊匹配,最常用

    • headers:按header匹配,很少用

  • RoutingKey:路由键

  • ACK:消息确认机制

2. 你在Go项目里怎么用(贴合简历)

使用amqp091-goSDK对接RabbitMQ。

场景:下单业务解耦。用户下单成功之后,订单主库落库完成,发送一条消息到MQ;消费者监听消息,异步去做:发送短信通知、增加用户积分、生成订单日志。

好处:短信、积分这些非核心逻辑失败,不会影响下单主流程;大促流量突增的时候,消息堆积在队列里,消费者慢慢消费,不会直接打垮积分、短信服务,实现削峰。

3. 高频面试题 + 回答 + 踩坑故事

(1)怎么保证消息不丢失?

回答:消息丢失分三个环节,我们分别做保障:

  1. 生产者端:开启生产者确认(publisher confirm),消息成功投递到交换机才认为发送成功,失败就重试;

  2. MQ服务端:交换机、队列设置持久化,消息设置持久化;

  3. 消费者端:关闭自动ACK,手动ACK,业务逻辑处理成功之后,再手动提交ACK;处理失败就NACK,消息重回队列或者丢到死信队列。

(2)消息重复消费怎么办?

回答:MQ本身不保证消息只投递一次(at-most-once、at-least-once),RabbitMQ是至少投递一次,所以一定会有重复。

解决方案:业务幂等。我们用业务唯一ID(订单号),消费之前先查数据库,这个订单号已经处理过,直接跳过,不重复执行业务。数据库唯一索引兜底。

(3)消息积压怎么办?

回答:先排查根源:消费者消费速度跟不上生产速度。

处理步骤:

  1. 先看消费代码有没有慢SQL、阻塞逻辑,优化消费逻辑;

  2. 横向扩容消费者实例,增加消费能力;

  3. 紧急情况,把消息转发到死信队列,避免正常队列爆满;

我们业务遇到过一次,消费逻辑里面写了同步调用第三方接口,第三方超时阻塞,消费卡主,消息疯狂堆积,后面改成异步调用第三方,设置超时时间,解决了。

(4)死信队列是什么?

回答:消息超时、被拒绝NACK、队列满了,消息会变成死信;死信队列就是专门接收死信的队列,用来做失败消息的重试、告警,避免消息丢了无声无息。

(5)进阶问题:消息顺序、事务消息

问:怎么保证消息顺序?

👉 兜底:我们当前业务不需要严格顺序消息,所以没有做顺序保障;如果要严格顺序,需要同一个路由key绑定同一个队列,单消费者消费。

问:RabbitMQ事务消息?

👉 兜底:事务消息性能差,我们业务没有使用。


三、Protobuf(protobuf,tRPC/rpcx底层协议,重点!)

1. Protobuf是什么

Google推出的结构化数据序列化协议,和JSON、XML是同类,作用是把结构体序列化成二进制字节流,用于服务之间传输数据。

JSON是文本格式,人可读;Protobuf是二进制格式,体积更小、编解码速度更快,适合内网微服务RPC通信。

你的rpcx、tRPC框架,默认就是用protobuf定义接口入参、出参。

2. 项目里怎么用

  1. 新建.proto文件,定义message结构体、service服务、rpc接口;

  2. 使用protoc + go插件,自动生成Go的结构体代码、RPC桩代码;

  3. 服务调用的时候,直接用生成好的结构体,框架自动完成序列化、反序列化。

区分:对外给前端的REST接口,我们用JSON;内部服务之间RPC调用,统一用protobuf。

3. 高频面试题

(1)Protobuf对比JSON优缺点

回答:

优点:

  1. 二进制,包体积更小,网络传输流量更少;

  2. 编解码速度更快,性能更好;

  3. 强类型,proto文件定义字段,自动生成代码,自带字段校验,减少参数错误;

  4. 天然支持向前、向后兼容。

    缺点:

  5. 二进制,抓包调试不方便,不能直接看懂内容;

  6. 版本变更有规范,不能随便删除、修改字段编号。

(2)Protobuf怎么做到版本兼容?(非常高频)

回答:核心规则:字段编号是唯一标识

  • 新增字段:直接加新的编号,老版本代码会忽略这个新字段,新版本能识别老数据,兼容;

  • 不要修改已经存在的字段编号;

  • 不要删除已经在用的字段(如果废弃,只能标记reserved保留编号,不能给别的字段复用);

  • 字段类型不要随便改。

(3)踩坑故事(背诵)

之前踩过坑:改proto的时候,直接把老字段删掉,复用了编号,导致线上新旧服务之间序列化解析错乱,服务报错。后面定下规范:废弃字段必须写reserved,不允许复用编号。

(4)进阶:protobuf的编码原理、zigzag编码

👉 兜底:底层的varint、zigzag编码原理我看过文档,但是项目开发中主要是写proto定义、生成代码,没有深入改造底层编解码逻辑。


四、Kubernetes(K8s,最难,你只抓开发视角,不要往运维方向说)

划重点:面试官是后端岗,他不考你怎么写复杂statefulset、调度策略、网络插件,他想看你知不知道:你的Go服务怎么在K8s上跑起来。

1. Docker 和 K8s 分别是什么

Docker:把应用、依赖、配置打包成镜像,一次打包,到处运行;隔离单个应用容器。

Kubernetes(K8s):容器编排平台,用来批量管理大量Docker容器。自动实现:服务自愈、滚动发布、副本扩缩容、内部负载均衡、资源调度。

2. 核心名词(只背这几个,别的不问不说)

  • Pod:K8s最小调度单元,一个Pod里面可以放一个或多个容器,我们业务一般一个Pod一个Go服务容器;

  • Deployment:管理无状态服务(你的Go后端服务就是无状态),控制Pod副本数量、滚动更新、回滚;

  • Service:给一组Pod提供统一访问入口、内部负载均衡,Pod漂移重启IP变了,Service名字不变;

  • ConfigMap:放配置文件(yaml、json),不写死在镜像里面;Secret:放密码、密钥等敏感信息;

  • Node:集群里的一台服务器;

  • Namespace:命名空间,用来隔离环境(dev、test、prod)。

3. 你在项目里做什么(贴合简历,保命话术)

我作为后端开发,主要负责:

  1. 编写Go服务的Dockerfile,打包镜像,推到镜像仓库;

  2. 配合运维,编写简单的Deployment、Service的yaml文件,定义副本数、资源限制、健康检查;

  3. 上线之后,会用kubectl命令:查看pod状态、看pod日志、describe看pod启动失败原因,排查业务侧问题;

  4. 新版本上线,配合运维做滚动发布;如果发布出问题,协助回滚。

集群搭建、网络插件、存储、调度策略、集群监控这些,是运维同学负责维护。

4. 高频面试题 + 回答

(1)Deployment滚动更新是什么?

回答:新版本发布的时候,Deployment会逐步创建新版本Pod,等新Pod就绪之后,再销毁旧版本Pod,分批替换,服务不会中断;如果新版本健康检查失败,会停止发布,我们可以执行回滚,切回旧版本。

(2)什么是健康检查?

回答:livenessProbe存活探针(判断容器是不是挂了,挂了就重启Pod);readinessProbe就绪探针(判断服务能不能接收流量,没就绪就不会把流量转发给它)。我们Go服务会写/health健康接口,给K8s做探针。

(3)踩坑故事(背诵)

之前踩过坑:Dockerfile没有做资源限制,上线K8s之后,服务内存飙高,把节点资源打满,Pod被OOM杀掉。后面在yaml里面加上request和limit资源限制,同时优化代码内存泄漏问题。还有一次健康接口写错,导致新Pod一直就绪失败,发布卡住。

(4)进阶问题:StatefulSet、DaemonSet、HPA、Ingress、PV/PVC

👉 兜底:这些资源我了解概念,但是日常业务开发很少写,由运维统一维护配置。


五、额外配套:面试官连环追问的统一话术(防穿帮神器)

  1. 问底层源码、集群调优、运维部署细节:

“这块我们是运维统一维护,我业务侧主要是接入使用,解决业务层面问题,底层原理我有学习,还在持续深耕。”

  1. 问:你这个项目里面,中间件是单机还是集群?

“线上是集群高可用部署,运维维护,我开发的时候本地是单机版本做开发调试。”

  1. 问:你有没有做过压测?

“我做过简单接口压测,发现过Redis热点key、MQ消费慢的问题,完整全链路压测是测试和运维配合一起做。”

六、自我介绍版本(把这一段背熟,开头直接锁死你的定位,避免面试官往运维/中间件专家方向深挖)

我主要做Go后端开发,基于Gin、Beego开发RESTful接口,tRPC、rpcx做内部微服务通信,使用protobuf定义RPC接口协议;

中间件上,Redis用来做热点数据缓存、分布式锁、计数器;RabbitMQ用来异步解耦、削峰,处理下单之后的通知、积分这类非核心流程;

服务使用Docker打包镜像,部署在K8s集群,我这边负责Dockerfile、基础yaml编写、上线后日志排查、简单故障定位,集群运维由运维同学负责;

同时我也在用Cursor、Codex这类AI编程工具,做过RAG知识库、Agent Skill编排的落地实践。


如果你想要,我还可以给你做一份【一页纸精简背诵版】,全部压缩成短句,适合手机上快速翻看,面试前突击。

本文作者:枣子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!