面灵AI→

小红书 AI 小工具 leader 面:MQ 异步化、分库分表热点与稳定性

轮次
leader 面
时间
2026-10
来源
牛客网

《面试题目》

  1. 自我介绍。
  2. 简历里那个 MQ 解耦、QPS 提升的项目,是生产项目还是自己做的?
  3. 那其实你就是把同步做了一次异步转换,对吧?
  4. 原来同步失败前端能感知,异步失败你怎么知道,怎么保证它稳定运行?
  5. 你这个重试是无限制的吗?
  6. 如果失败了,你会漏掉消息吗?
  7. 这个过程怎么防止重复投递?
  8. 如果有顺序要求,你觉得它能做到吗?
  9. QPS 再提高 10 倍,你觉得瓶颈在哪里?
  10. 数据库层面可以解决它的问题吗?
  11. 按设备去分库分表,数据会均衡吗,还是会有热点?
  12. 有热点、有数据倾斜,你觉得有办法解吗?
  13. 有些表数据量特别大,你觉得还有办法继续优化吗?
  14. Redis 缓存你用过的数据结构有哪些?
  15. 如果用 Redis 做一个榜单,你会怎么做?
  16. 你还用了 Redis 的 bitmap 对吧,主要用在什么场景?
  17. 当时为什么选它,是因为量级很大吗,直接用数据库不行吗?
  18. 这个项目对你来说最大的难点是什么?
  19. 结合你现在的认知,从架构角度看这个系统有什么不足?
  20. 上线之后一直有人运维,稳定性这块你们做了哪些事?你怎么觉得这个系统还在稳定地跑?
  21. 半夜没人用的时候,哪些指标能提前发现它出了问题?
  22. 你平时有看过并发工具的源码吗,学到过什么?
  23. 你现在会用 AI 来做一些什么事情?
  24. 你怎么判断 AI 写的到底对不对?
  25. 手撕:LRU。
  26. 你最大的优点和缺点是什么?
  27. 你实习的时间计划是什么,最快多久能到岗,对实习地点有要求吗?
  28. 反问。

《参考解析》

同步改异步后,失败怎么被感知:同步调用的失败顺着返回值就到前端了,异步之后失败发生在消费者里,必须自己补上「失败可见」。做法是三层:消费侧捕获异常后不吞掉,按错误类型分流(可重试的进延迟重试队列,业务性失败进死信表并告警);对账——定时用业务主表与消息处理表比对,找出「有请求无结果」的孤儿记录并补;端到端可观测——每条消息带 traceId,从投递到落库全链路能串起来,看板上能看到积压量、消费失败率、P99 处理时长。重试必须有上限和退避:指数退避 + 最大次数 + 死信兜底,否则坏消息会把队列堵死。

重复投递与幂等:MQ 普遍是「至少一次」,重复无法避免,只能靠消费侧幂等。幂等键的选法很关键——用业务唯一键(订单号、请求 id、设备 + 时间窗)而不是消息 id,因为消息重投和业务重发是两回事。落地一般是一张幂等表(唯一索引 + 状态机)或在 Redis 上做 setnx + 过期,前者强一致但多一次写,后者快但要考虑过期语义。

顺序性:全局有序只能单分区单消费者,吞吐会崩;实际做法是按业务键分区——同一订单/同一设备的消息 hash 到同一分区,分区内单线程消费,就得到「局部有序 + 整体并行」。还要注意重试队列会破坏顺序:同 key 的消息重试时不能被后来的消息插队,要么阻塞住该 key 的后续消息,要么重试消息回原分区排队。

QPS 再涨十倍的瓶颈在哪:按链路逐段推。入口是带宽与连接数,然后是应用层的线程/协程与 GC 停顿,接着是 DB 的连接数与写入能力,最后是 Redis 的单分片 QPS 和网络往返。真正先崩的通常不是「算不过来」,而是连接池打满 + 慢查询 + 锁等待三者互相放大。数据库层能缓解的招是:写入合并/批量提交、冷热分离、读写分离、按业务键分片;但分片会引入跨片查询和分布式事务的成本,得算清楚。

分片热点:按设备号 hash 理论上均匀,现实里会偏——头部设备/大客户贡献了不成比例的流量,hash 只保证「键分散」不保证「负载分散」。解法分两类:打散(对热点键加后缀拆成 N 份,查询时扇出合并)、隔离(热点单独建库或单独一条链路,避免拖累长尾)。监控上要能画出分片间的 QPS/容量分布,不能只看总量均匀。

Redis 榜单与 bitmap 的选型:实时榜单用 ZSET——score 是分数,ZINCRBY 累加、ZREVRANGE 取 TopN,日榜/周榜可以用 key 加时间后缀,配合过期自动清理。bitmap 适合「海量 id 的布尔状态」:签到、是否活跃、是否已读,一个用户一天一位,1 亿用户一个月只要几百 MB;用 SETBIT/BITCOUNT/BITOP 做统计和交集。选它而不是数据库的原因是空间和原子性:数据库存一行一状态,同样的量级存储和统计成本高一个数量级;但它的代价是只能表达布尔、不支持复杂查询,需要额外的维度就得换结构(比如 HLL 做去重计数)。

稳定性怎么证明它在跑:不能只说「没出过事」。要有分层指标:资源层(CPU、内存、连接数、GC 次数与停顿)、应用层(QPS、P95/P99 延迟、错误率、线程池队列长度)、业务层(下单成功率、消息积压量、对账差异数)。深夜没人用时最该看的是趋势型指标:内存是否缓慢上涨(泄漏)、连接数是否只增不减(泄漏)、定时任务是否按时执行、慢查询数量是否在爬。再看告警是否真的会响——定期人为制造一次失败验证告警链路,比堆一堆没人看的监控有用。

手撕 LRU:哈希表 + 双向链表,get 命中就把节点移到头部,put 超容量就删尾节点。要点是哨兵头尾节点省掉边界判断、get/put 都 O(1)、注意「put 已存在的 key 要更新值并移到头部」。