面灵AI→

顺丰一面:缓存、索引、消息与 Agent 基础八股

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

  1. 你对 AI 全栈的理解是什么?
  2. 缓存三剑客(穿透、击穿、雪崩)分别是什么?怎么解决?
  3. Redis 的内存过期策略有哪些?
  4. MySQL 索引哪些情况下会失效?
  5. 消息重试和消息丢失怎么处理?
  6. 你对 Agent 的理解是什么?
  7. CAS 是什么?有什么问题?
  8. 线程池的原理和核心参数?
  9. 事务的隔离机制有哪些?
  10. 拷打实习和项目。

《参考解析》

缓存三剑客:① 穿透——查询根本不存在的数据,缓存永远不命中,请求直打数据库。解法是布隆过滤器拦截非法 Key,或对空结果缓存一个短 TTL 的空值(注意攻击者每次换 Key 会让空值缓存膨胀,需要限制 key 的生成规则或对来源限流)。② 击穿——某个热点 Key 过期瞬间大量并发同时打到数据库。解法是互斥锁(只让一个请求去重建)或逻辑过期(不设物理过期,异步刷新,读到旧值时返回旧值)。③ 雪崩——大量 Key 同时过期或缓存整体不可用。解法是过期时间加随机抖动、多级缓存、以及 Redis 高可用(哨兵/集群)。

Redis 过期策略:惰性删除(访问时检查是否过期,过期就删)+ 定期删除(每 100ms 随机抽取一批设置了过期时间的 Key 检查并删除)。两者结合的原因是纯惰性会让内存长期不释放,纯定期扫描又太耗 CPU。内存达到 maxmemory 时的淘汰策略有八种:noeviction(写入报错)、allkeys-lru/lfu/random/ttl、volatile-lru/lfu/random/ttl。做缓存一般用 allkeys-lru,做”有过期时间的会话”用 volatile-*。注意 LRU 在 Redis 里是近似实现(采样后淘汰),LFU 更适合有明显热点的场景。

消息重试与不丢:不丢要三段保证——生产者确认(Kafka acks=all + 重试、RocketMQ 同步发送)、Broker 持久化与多副本、消费端手动 ACK(处理成功再提交位点)。重试要有纪律:指数退避 + 随机抖动,避免重试风暴;设置最大重试次数,超过进死信队列并告警;重试必须幂等(否则会重复扣款、重复发货),靠业务唯一 ID + 去重表/唯一索引 + 状态机式条件更新实现。另外注意重试的语义边界:网络超时场景下”重试”可能造成重复执行,所以幂等键要在第一次请求前就生成并复用。

CAS 及其问题:Compare-And-Swap 是一条 CPU 原子指令,比较内存值与期望值,相等才写新值,用来实现无锁并发(AtomicInteger、AQS 的入队)。三个经典问题:① ABA——值从 A 变 B 又变回 A,CAS 会误判为没变,解法是加版本号 AtomicStampedReference;② 自旋开销——高竞争下大量线程空转,LongAdder 用分段降低竞争;③ 只能保证单个变量的原子性,多变量需要借助锁或封装成对象。

线程池与隔离级别:线程池七参数——核心线程数、最大线程数、空闲存活时间、时间单位、工作队列、线程工厂、拒绝策略;执行流程是”核心线程未满就新建 → 队列未满就入队 → 最大线程未满就新建 → 触发拒绝策略”。生产上强调用有界队列(LinkedBlockingQueue 默认容量是 Integer.MAX_VALUE,会把内存吃满),并按业务隔离线程池。事务隔离级别是读未提交(脏读)、读已提交(不可重复读)、可重复读(幻读,MySQL 默认)、串行化;MySQL 靠 MVCC 实现一致性读、靠 next-key lock 压住当前读的幻读。