分享一下最近训练营的朋友在某科技电商平台的一面面经,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,备注:面经。