写点什么

坐标山东青岛,12~20K 面试强度

作者:王中阳Go
  • 2025-09-22
    北京
  • 本文字数:4168 字

    阅读完需:约 14 分钟

坐标山东青岛,12~20K面试强度

分享一下最近训练营的朋友在某科技电商平台的一面面经,Base 在青岛,感兴趣的朋友可以学习一下,下面是该岗位的 JD:


面经详解

1. kafka 与 rabbitMQ 有什么不同,例如持久化?

从消息模型、持久化机制、适用场景等维度来看,两者差异显著:


  • 消息模型:Kafka 基于“发布-订阅”模型,消息按主题(Topic)分类,消费者通过消费组订阅主题,更适合大规模消息分发;RabbitMQ 支持多种交换机类型(direct、topic、fanout 等),路由规则灵活,适合复杂业务路由场景。

  • 持久化:Kafka 默认将消息持久化到磁盘日志文件,通过分区复制保证可靠性,持久化性能高(顺序写入),适合海量数据存储;RabbitMQ 需手动配置持久化(队列、消息均需设置),消息存储在内存+磁盘,单节点持久化性能一般,但支持镜像队列实现高可用。

  • 吞吐量:Kafka 吞吐量远高于 RabbitMQ,因采用批量处理、顺序 IO,适合日志采集、大数据场景;RabbitMQ 更侧重低延迟和灵活路由,适合业务消息(如订单通知)。

2. 对 Redis 和 RabbitMQ 展开讲讲这个分片集群、缓存是怎么设计的,以及用 rabbitMQ 异步解耦是用在哪些功能的?

  • Redis 分片集群与缓存设计:

  • 分片集群:采用 Redis Cluster 模式,将数据按哈希槽(16384 个)分片到不同节点,通过主从复制和哨兵实现高可用,解决单节点内存上限问题。

  • 缓存设计:采用“本地缓存(如 Go 的 sync.Map)+ Redis 分布式缓存”的多级缓存架构;缓存策略上,用 LRU 淘汰策略,设置合理过期时间,通过布隆过滤器防穿透,互斥锁防击穿。

  • RabbitMQ 分片与异步解耦:

  • 分片:RabbitMQ 本身无原生分片,通过“多队列+负载均衡”模拟分片(如按用户 ID 哈希到不同队列),结合镜像队列保证数据不丢失。

  • 异步解耦场景:订单创建后,通过 RabbitMQ 异步通知库存扣减、支付回调、物流生成、消息推送等服务,避免同步调用导致的链路过长和级联失败。

3. RabbitMQ 消费速度是怎么样的?有没有遇到消息积压的情况,以及你当时是怎么处理解决的?

  • 消费速度:默认情况下,消费速度取决于消费者数量、单条消息处理耗时、预取计数(prefetch count)。单消费者处理速度通常为每秒几十到几百条(视业务复杂度),增加消费者(同队列多消费者负载均衡)可线性提升速度。

  • 消息积压及处理:

  • 曾遇到过因下游服务故障导致的积压(队列消息量达百万级)。解决措施:

  • 临时扩容:快速部署多个临时消费者实例,仅处理积压消息(忽略新消息);

  • 优化消费逻辑:简化非核心处理步骤(如暂时关闭日志打印、非必要校验);

  • 流量控制:通过 RabbitMQ 的 flow 控制机制限制生产者速度,避免积压加剧;

  • 死信队列:将处理失败的消息转发到死信队列,后续人工处理,避免阻塞正常消费。

4. 缓存雪崩,描述下当时遇到了什么问题,以及你们后来的方案是怎么处理的?

  • 问题场景:某次大促前,大量商品缓存设置了相同过期时间,到期后全部失效,导致所有请求直接打向 MySQL,DB 连接数瞬间爆满,出现大量超时,核心接口响应时间从 50ms 增至 5s+,部分服务熔断。

  • 解决方案:

  • 过期时间随机化:在基础过期时间(如 2 小时)上增加 0-30 分钟随机值,避免缓存同时失效;

  • 多级缓存:增加本地缓存(如 Go 的 singleflight+sync.Map),热点商品先查本地再查 Redis;

  • 熔断降级:用 Sentinel 配置 DB 访问熔断阈值,当超时率超 50%时,返回默认数据或降级提示;

  • Redis 集群:升级 Redis 为 3 主 3 从 Cluster 模式,避免单节点故障导致缓存整体不可用。

5. Redis 缓存商品的信息,这一个单个信息的大小大概是多少?百万级 SKU 用了多少内存?Redis 版本用的是什么版本?

  • 单个商品信息大小:取决于字段多少(如 ID、名称、价格、库存、图片 URL 等),用 Hash 结构存储时,单个商品约 500 字节-2KB(简单商品 500 字节左右,含多规格、多图片的复杂商品约 2KB)。

  • 百万级 SKU 内存:按平均 1KB/个计算,约 1000KB×100 万=1000MB(1GB),加上 Redis 自身开销(如哈希槽、连接信息),实际占用约 1.2-1.5GB。

  • Redis 版本:生产环境用 5.0.x,因稳定性好,支持 Stream 数据结构(用于部分消息队列场景),且兼容大部分客户端。

6. Redis 的其他数据结构还有应用过吗?

有,根据业务场景选择不同结构:


  • Hash:存储商品详情(field 为属性名,value 为属性值),支持部分字段更新,节省内存;

  • List:实现简单消息队列(如订单待支付通知),用 LPUSH+BRPOP 操作;

  • Set:用户标签去重(如“已浏览商品 ID 集合”),支持交集/并集运算(如推荐相似商品);

  • Sorted Set:商品销量排行榜(score 为销量,member 为商品 ID),用 ZADD+ZREVRANGE 获取 TopN;

  • Bitmap:用户签到功能(1 字节存 8 天签到状态),用 SETBIT+BITCOUNT 统计;

  • HyperLogLog:统计商品详情页 UV(去重计数),用 PFADD+PFCOUNT,内存效率极高。

7. mysql 优化

从索引、SQL、架构等层面优化:


  • 索引优化:建立联合索引(遵循最左前缀原则),避免冗余索引;对大表的 TEXT 字段建前缀索引;删除未使用索引(通过 slowlog 和 sys.schema_unused_indexes 排查)。

  • SQL 优化:禁止 SELECT *,只查必要字段;避免在 WHERE 子句中对索引列做函数/运算(如 DATE(create_time)='2023-01-01'会导致索引失效);用 JOIN 代替子查询,减少临时表生成。

  • 架构优化:分库分表(按用户 ID 哈希分库,按时间范围分表);读写分离(主库写,从库读,用 MyCat 中间件路由);表结构优化(大字段拆分到子表,如商品详情单独存表)。

  • 其他:开启查询缓存(简单场景);调整 MySQL 配置(如 innodb_buffer_pool_size 设为物理内存 50%-70%,max_connections 根据并发调整);定期 OPTIMIZE TABLE 优化表碎片。

8. 数据中台中,如果有一些商品需要关闭掉(例如被 315 曝光),获取商品有一个关键词库,如果要设计实现这个功能,你有什么算法方案吗?

核心是高效匹配商品与关键词库,方案如下:


  • 算法选择:采用“前缀树(Trie)+ 布隆过滤器”组合方案。

  • 前缀树:将关键词库构建成 Trie 树(如“过期”“伪劣”等关键词),商品名称/描述分词后,通过 Trie 树快速匹配是否包含敏感词(时间复杂度 O(L),L 为字符串长度);

  • 布隆过滤器:对关键词库做哈希映射,先通过布隆过滤器快速判断商品是否“可能包含敏感词”,过滤掉 99%的非敏感商品,再用 Trie 树精确匹配,减少计算量。

  • 工程实现:关键词库定期更新(每天凌晨全量同步),Trie 树和布隆过滤器预加载到内存;商品创建/更新时触发实时校验,命中则标记为“待关闭”,由中台服务异步处理关闭逻辑。

9. 分布式事务,是用什么工具实现的?

主要用两种方案,根据业务场景选择:


  • 最终一致性方案:基于“本地消息表+RabbitMQ”实现。步骤:1. 本地事务执行时,同时写入消息表(状态为“待发送”);2. 事务提交后,异步将消息表中的消息发送到 RabbitMQ;3. 下游服务消费消息并执行本地事务,回调通知上游更新消息状态;4. 定时任务扫描未确认消息,重试发送。适合非核心场景(如订单创建后通知积分服务)。

  • 强一致性方案:用 Seata 的 AT 模式。通过全局事务 ID 协调各分支事务,基于 undo log 实现回滚,适合核心场景(如支付与扣库存的一致性)。优势是接入简单(注解 @GlobalTransactional),性能满足大部分业务。

10. 下单流程、库存超卖、lua 脚本是跟订单接口怎么交互的?

  • 下单流程:前端请求→校验商品状态→扣减库存→创建订单→返回订单 ID。

  • 库存超卖问题:并发下单时,若先查库存再扣减(非原子操作),会导致超卖。解决方案:用 Redis+Lua 脚本实现原子性扣减。

  • Lua 脚本与订单接口交互:订单接口中,调用预定义的 Lua 脚本(逻辑:1. 检查 Redis 中库存是否≥购买数量;2. 若满足,扣减库存并返回 1;3. 否则返回 0)。Go 代码中通过 Redis 客户端(如 redigo)执行脚本,根据返回值判断是否允许下单。Lua 脚本保证“查库存+扣库存”原子性,避免并发问题。

11. 订单未支付,失效 30 分钟怎么实现,这块为什么不用 kafka 要用 rabbitMQ

  • 实现方式:用 RabbitMQ 的延迟队列(死信交换机+TTL)。步骤:1. 创建订单时,发送一条带 30 分钟 TTL 的消息到“延迟队列”(该队列无消费者);2. 消息过期后,自动转发到“死信交换机”,绑定到“订单失效队列”;3. 消费者监听“订单失效队列”,收到消息后执行订单取消、库存回补逻辑。

  • 选择 RabbitMQ 而非 Kafka 的原因:Kafka 原生不支持延迟队列,需通过“时间轮+定时任务”模拟,实现复杂且精度低;而 RabbitMQ 通过 TTL+死信交换机可直接实现延迟队列,配置简单,且支持消息持久化和确认机制,适合订单这类对可靠性要求高的场景。

12. 分库分表时,涉及到历史数据迁移的问题,具体方案?

采用“双写+校验+切换”的平滑迁移方案,步骤如下:


  • 准备阶段:搭建分库分表中间件(如 ShardingSphere),定义分片规则(如按订单创建时间分表);开发数据迁移工具(Go 编写,基于 MySQL binlog 同步)。

  • 双写阶段:1. 旧库写入时,同时通过消息队列通知迁移工具写入新库;2. 全量迁移历史数据(按批次同步,每批 10 万条,避免锁表);3. 实时同步增量数据(监听 binlog,保证新旧库数据一致)。

  • 校验阶段:对比新旧库数据(通过 MD5 哈希比对关键字段),差异率低于 0.01%视为合格;持续监控 1 周,确保增量同步无延迟。

  • 切换阶段:灰度切换(先切 10%流量到新库),观察无异常后全量切换;切换后保留旧库 1 个月,用于数据核对和回滚。

13. 在微博子公司的项目中,你说到接口响应性能提升 200%,降低 40% 服务器资源消耗 这块展开讲一讲,怎么实现 以及响应提升 200%是从哪个方面,你们的 QPS 大概是什么量级的?

  • 实现方案:核心是“多级缓存+异步化+代码优化”。

  • 多级缓存:新增本地缓存(sync.Map,缓存热点数据,如首页推荐商品)→ Redis 分布式缓存(全量商品数据)→ CDN(静态资源如商品图片),减少 90%的 DB 访问。

  • 异步化:将非核心逻辑(如浏览量统计、日志上报)通过 RabbitMQ 异步处理,接口同步逻辑从 5 步减至 2 步。

  • 代码优化:用 Go 的 pprof 分析瓶颈,优化序列化(用 protobuf 替代 JSON,减少 30%耗时);减少锁竞争(用原子操作替代互斥锁)。

  • 响应提升 200%:指接口平均响应时间从优化前的 150ms 降至 50ms((150-50)/50=200%),主要来自缓存穿透减少(DB 访问量降 90%)和序列化效率提升。

  • QPS 量级:优化前峰值 QPS 约 5000,优化后支撑 1.5 万+ QPS,服务器从 10 台减至 6 台,资源消耗降低 40%。

14. 印象最深的问题是什么?怎么解决的?最终优化结果怎么样?为什么觉得这个印象深刻的?

坚定不移,听话照做,按部就班,早日上岸!

加我微信,免费领面经,升职加薪:wangzhongyang1993,备注:面经。


点击这里查看原文链接,我的公众号更新了570多篇原创内容,欢迎关注,第一时间查看干货内容。

发布于: 刚刚阅读数: 2
用户头像

王中阳Go

关注

靠敲代码在北京买房的程序员 2022-10-09 加入

【微信】wangzhongyang1993【公众号】程序员升职加薪之旅【成就】InfoQ专家博主👍掘金签约作者👍B站&掘金&CSDN&思否等全平台账号:王中阳Go

评论

发布
暂无评论
坐标山东青岛,12~20K面试强度_Go_王中阳Go_InfoQ写作社区