八股文 / 2026 2–8 月

2026 年 2–8 月后端面经八股整理

本文汇总 2026 年 2–8 月后端与 AI 工程面经,共 189 道传统技术问题。题目直接按技术类型归档,不再按月份拆分章节。

这批题型从基础原理延伸到工程追问:缓存与数据库一致性、MQ 可靠性、线上延迟排查、多节点部署、前端性能和 AI Infra 都应优先准备。

Java、JVM 与 Spring

1. HashMap 为什么是线程不安全的?

来源:字节 AI 全栈研发二面

  • 多线程同时 put 触发扩容时,可能导致链表成环(JDK 7)或数据覆盖(JDK 8)
  • size++ 非原子操作,并发写导致计数不准
  • modCount 检测到并发修改会抛 ConcurrentModificationException,但这是 fail-fast 不是线程安全

2. 怎么判断单例 Bean 是否线程安全?怎么解决?

来源:字节 AI 全栈研发二面

判断:如果 Bean 内部存在可变的共享变量,且多线程可以对其修改,则不是线程安全的。

解决方案:

  • 无状态设计(最佳):Bean 不持有可变字段,只有方法调用
  • ThreadLocal:每个线程持有独立副本
  • 改为多例(@Scope(“prototype”)):每次注入新实例
  • 加锁:synchronized / ReentrantLock(性能最差,最后手段)

3. 一个进程中有 1000 个 ConcurrentHashMap 并发更新,为什么会 GC 频繁?怎么解决?

来源:杭州滴滴 CTO 面

原因:每个 CHM 扩容时创建新数组(2倍),1000 个 CHM 同时扩容 → 大量大对象分配 → 频繁触发 GC(尤其是 Young GC 晋升到 Old 区)

解决

  • 预估容量初始化(initialCapacity),减少扩容次数
  • 控制 CHM 数量,能合并就合并
  • 使用对象池或预分配策略
  • 调整 GC 参数(增大 Young 区、使用 G1 的 Region 机制)

4. JVM 内存区域和 GC 类型?G1 和 CMS 区别?

来源:杭州滴滴 CTO 面 / 嘉立创一面

运行时数据区

  • 堆(Heap):对象实例,GC 主战场
  • 方法区/元空间(Metaspace):类信息、常量池
  • 虚拟机栈:线程私有,方法调用栈帧
  • 本地方法栈:Native 方法
  • 程序计数器:当前执行字节码地址

G1 vs CMS

维度 CMS G1
目标 最短停顿时间 可预测停顿时间(设目标暂停毫秒)
内存布局 连续分代(Young/Old) Region 化(不连续,每个 Region 可以是任何代)
碎片 标记-清除,有碎片 标记-整理(Region 级别 compact),无碎片
Full GC 退化为 Serial Old,STW 很长 Mixed GC 逐步回收,Full GC 是最后兜底

5. ThreadLocal 原理?内存泄漏怎么回事?

来源:杭州滴滴 CTO 面

  • 每个线程持有一个 ThreadLocalMap(key=ThreadLocal 引用,value=存储值)
  • get/set 操作只在当前线程的 map 中进行,天然线程隔离

内存泄漏

  • ThreadLocalMap 的 key 是弱引用(WeakReference),GC 后 key 变 null
  • 但 value 是强引用,如果线程池中线程长期存活,value 永远不会被回收
  • 解决:用完务必调 remove()

6. Spring Bean 生命周期?三级缓存解决循环依赖?

来源:嘉立创一面

生命周期:实例化 → 属性注入 → Aware 接口回调 → BeanPostProcessor 前置 → InitializingBean/init-method → BeanPostProcessor 后置 → 使用 → DisposableBean/destroy-method

三级缓存

  • 一级:singletonObjects(完整 Bean)
  • 二级:earlySingletonObjects(半成品 Bean,已实例化未注入)
  • 三级:singletonFactories(Bean 工厂,用于创建代理对象)

流程:A 依赖 B,B 依赖 A → A 实例化后放三级缓存 → 注入 B 时发现需要创建 B → B 注入 A 时从三级缓存获取 A 的早期引用 → B 完成 → A 完成


7. SpringBoot 自动装配原理?

来源:嘉立创一面

  1. @SpringBootApplication 包含 @EnableAutoConfiguration
  2. @EnableAutoConfiguration 通过 @Import(AutoConfigurationImportSelector) 导入配置
  3. AutoConfigurationImportSelector 读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  4. 根据 @Conditional 注解条件判断哪些自动配置类生效
  5. 生效的配置类注册对应的 Bean 到容器

本质:约定优于配置 + SPI 机制 + 条件装配


8. 类加载的过程是什么?

来源:字节 AI 应用开发一面,7 月 21 日

JVM 经加载、验证、准备、解析、初始化得到可用类。加载读取字节码并创建 Class 元数据;验证保证格式和类型安全;准备为静态字段分配内存并设默认值;解析把符号引用转为直接引用;初始化执行类初始化方法。双亲委派减少核心类被重复或恶意替换,但 SPI、容器隔离等场景会有受控打破。


9. Spring AOP 在哪些情况下会失效?

来源:滴滴 AI Agent 后端面经,7 月 23 日

典型情况包括同类内部 this.method() 自调用绕过代理、目标方法不可被代理覆盖、对象不是 Spring 管理的 Bean、切点表达式未命中,以及代理类型与调用方式不匹配。排查先确认注入的是代理对象、实际调用路径和事务/切面日志;自调用可通过拆分 Bean 或显式走代理解决,但不要为套 AOP 扭曲类职责。


10. Spring 如何处理循环依赖?

来源:滴滴 AI Agent 后端面经,7 月 23 日

单例 Bean 的 setter/字段循环依赖可通过提前暴露对象工厂和早期引用解决,三级缓存还要保证 AOP 场景拿到一致代理。构造器循环依赖在对象实例化前就互相等待,无法用早期引用解决。更重要的工程结论是:循环依赖通常提示职责边界不清,应优先重构,而不是依赖容器开关掩盖设计问题。


11. Spring MVC 的请求处理流程是什么?

来源:滴滴 AI Agent 后端面经,7 月 23 日

请求进入 DispatcherServlet,通过 HandlerMapping 找到处理器和拦截器链,再由 HandlerAdapter 完成参数解析并调用 Controller。返回值经消息转换器写 JSON,或由视图解析器渲染;异常交给异常解析器。过滤器位于 Servlet 容器层,拦截器位于 MVC 调用链,两者边界不同。


12. CMS 垃圾收集器的流程和问题是什么?

来源:滴滴 AI Agent 后端面经,7 月 23 日

CMS 经初始标记、并发标记、重新标记和并发清除,目标是缩短停顿。它与应用并发工作会消耗 CPU,清除阶段产生空间碎片,并发期间新产生的垃圾要到下次处理;预留空间不足可能发生并发模式失败并退化为长停顿。新版本 JDK 已移除 CMS,面试时应同时了解 G1/ZGC 的替代背景。


13. 面向对象的三大特性是什么?抽象类和接口怎么选?

来源:某 Java 岗,8 月 12 日

  • 封装:隐藏内部状态,通过稳定接口维护对象不变量。
  • 继承:复用和扩展已有行为,但会形成较强的父子耦合。
  • 多态:面向抽象编程,由运行时实际类型决定具体行为。

抽象类适合表达“同一类对象的共同状态和默认实现”,可以有构造器、成员变量和非抽象方法;接口适合表达“能力契约”,支持一个类实现多个接口。工程上优先组合和接口,只有确实存在稳定的 is-a 关系、需要共享状态或模板方法时再使用继承。


14. Java 运行过程中 GC 越来越频繁,如何排查?

来源:某 Java 岗,8 月 12 日;字节 Agent 开发一面,8 月 9 日

先区分是 Young GC 频繁还是 Full GC 频繁,再用数据定位:

  1. 查看 GC 日志和监控中的分配速率、晋升速率、停顿时间、堆各区域占用。
  2. jstat 观察趋势,用 jcmd/jmap 导出堆快照,用 MAT 分析大对象、引用链和疑似泄漏点。
  3. Young GC 频繁通常是对象分配过快或新生代太小;Full GC 频繁还要检查老年代增长、元空间、直接内存、显式 System.gc() 和晋升失败。
  4. 先修复对象生命周期、无界缓存、监听器未释放等代码问题,再考虑调整堆大小、分代比例或选择 G1/ZGC。只扩大堆会延后问题,不会消除泄漏。

15. JVM 为什么要划分新生代和老年代?

来源:字节 Agent 开发一面,8 月 9 日

依据是弱分代假说:绝大多数对象朝生夕死,少数对象会长期存活。新生代使用复制算法,回收频繁但只复制少量存活对象;经过多次回收仍存活的对象晋升老年代,老年代用标记整理等算法降低空间浪费。

分代的价值是让回收策略匹配对象生命周期,避免每次都扫描整个堆。需要补充的是,超大对象可能直接进入老年代,跨代引用还要靠记忆集和写屏障避免全堆扫描。


16. HashMap 在 JDK 7 和 JDK 8 中有哪些关键差异?

来源:百度内容营销与广告一面,8 月 12 日

JDK 7 的桶内结构主要是数组 + 链表,扩容迁移使用头插,在并发误用时可能形成环;JDK 8 改为尾插,并在冲突严重时把链表树化为红黑树,降低极端查询复杂度。JDK 8 还重写了扰动和扩容迁移逻辑,节点迁移时可根据新增的高位直接分到原位置或 原位置 + oldCap

无论哪个版本,HashMap 都不是线程安全容器。并发读写应使用 ConcurrentHashMap,而不是因为 JDK 8 不易形成环就把它当成线程安全。


并发与语言运行时

17. ConcurrentHashMap 底层的读写如何处理并发冲突?

来源:小红书 PE 后端一面

:value 和链表 next 指针设为 volatile,修改时直接操作公共内存,每个线程都能取到最新数据,读不需要加锁。

:先判断有无冲突——无冲突 CAS 插入;有冲突 synchronized 锁住头节点遍历链表插入。

扩容:支持多线程协同扩容。将原数组桶迁移到新数组(容量翻倍),系统把任务拆成 TransferRegion,线程领取后迁移。扩容期间读操作正常(最终一致),写操作协助扩容或等待。


18. Synchronized 和 ReentrantLock 怎么选?底层有什么区别?

来源:小红书 PE 后端一面

Synchronized

  • 使用简单,可加在代码块和方法上
  • 只能非公平锁
  • JDK 1.6 引入锁升级机制,底层通过对象头 Mark Word 实现:
    • 无锁 → 偏向锁(记录线程 ID)→ 轻量级锁(CAS 竞争)→ 重量级锁(ObjectMonitor 队列排队)

ReentrantLock

  • 基于 AQS 实现,底层有 state 记录重入次数 + FIFO 双向链式队列
  • 支持公平/非公平锁切换
  • 公平锁先查队列再竞争,非公平锁先尝试获取失败才入队

选型:简单同步用 synchronized(JVM 优化够好),需要公平锁/可中断/超时/多条件变量时用 ReentrantLock。


19. RPC 调用场景下线程池怎么配置?

来源:小红书 PE 后端一面

  • IO 密集型(RPC 调用为主):线程大多时间在等待外部响应,CPU 利用率低 → 线程数设大(通常 2N ~ 4N,N 为 CPU 核数)
  • CPU 密集型(计算为主):很少阻塞 → 线程数 = N+1,多了反而增加上下文切换开销

20. Go 的 GMP 调度模型?

来源:滴滴花小猪 Agent 开发一面

  • G(Goroutine):用户态协程,轻量级(2KB 初始栈)
  • M(Machine):操作系统线程,实际执行 G
  • P(Processor):逻辑处理器,持有本地运行队列

调度流程:P 从本地队列取 G 绑定到 M 执行。本地队列空时从全局队列或其他 P 偷取(work stealing)。G 阻塞时 M 释放 P,P 绑定新 M 继续调度。


21. Go Map 是线程安全的吗?sync.Map 底层?

来源:滴滴花小猪 Agent 开发一面

不安全。并发读写 map 会 panic(fatal error: concurrent map read and map write)。

sync.Map 底层

  • 两个 map:read(只读,无锁访问)+ dirty(读写,需加锁)
  • 读操作先查 read,命中直接返回(无锁);未命中才加锁查 dirty
  • 写操作加锁写 dirty
  • 当 dirty 被查询次数超过阈值时,提升为 read(dirty → read)
  • 适合读多写少场景;写多场景不如 RWMutex + map

22. volatile 关键字的作用?它保证的是什么有序性?

来源:杭州滴滴 CTO 面 / 嘉立创一面

  • 可见性:修改立刻刷回主内存,其他线程读取时从主内存取最新值
  • 禁止重排序:通过内存屏障防止指令重排(happens-before 规则)
  • 不保证原子性i++ 即使用 volatile 也不安全(读-改-写三步非原子)

volatile 保证的有序性是“对 volatile 变量的读写不会与其前后的操作重排序”,但不保证多个非 volatile 操作之间的顺序。


23. JDK 1.8 ConcurrentHashMap 为什么废弃分段锁?

来源:杭州滴滴 CTO 面

  • JDK 7 分段锁(Segment):将数组分为 16 段,每段独立加锁。问题:Segment 数量固定、内存浪费、锁粒度仍然偏粗
  • JDK 8 改为 CAS + synchronized 锁桶头节点
    • 锁粒度更细(一个桶一把锁 vs 一段多个桶共享锁)
    • 并发度更高(理论上并发度 = 桶数量)
    • 内存更省(去掉了 Segment 对象开销)

24. Java 线程池的执行流程是什么?

来源:字节 AI 应用开发一面,7 月 21 日

提交任务后,工作线程少于 corePoolSize 时先创建核心线程;否则尝试入队;队列满后再创建非核心线程,直到 maximumPoolSize;仍无法接收时执行拒绝策略。设计时必须说明队列是否有界、任务是否允许阻塞、拒绝后如何降级,以及如何监控活跃线程、队列等待和任务耗时。


25. 如果重新设计线程池,要考虑哪些模块?

来源:字节 AI 应用开发一面,7 月 21 日

需要任务队列、Worker 生命周期、核心/最大线程数、空闲回收、拒绝策略、异常隔离和关闭状态机。生产实现还要有背压、优先级/公平性、上下文传播、超时取消、指标与动态配置。难点不在“能启动线程”,而在竞争条件、任务丢失、关闭时序和过载保护。


26. 双重检查单例为什么需要 volatile

来源:字节 AI 应用开发一面,7 月 21 日

对象创建可抽象为分配内存、执行构造和发布引用。没有 volatile 时,发布引用可能被重排序到构造完成之前,其他线程读到非 null 引用却看到未初始化状态。volatile 提供可见性并禁止相关重排序;锁保证只有一个线程创建。更简单可靠的实现通常是枚举或静态内部类。


27. Go 的 Channel 和 Mutex 怎么选?

来源:滴滴 AI Agent 后端面经,7 月 23 日

Channel 适合传递数据所有权、编排流水线和事件通知;Mutex 适合保护共享内存中的短临界区。Channel 并不天然更快,也可能阻塞、泄漏 goroutine 或形成死锁。能把状态归属到单个 goroutine 时优先消息传递;简单计数器或共享结构用锁通常更直接。


28. GMP 调度中 P 没有本地任务时怎么办?

来源:滴滴 AI Agent 后端面经,7 月 23 日

P 优先从本地队列取 G,也会周期性检查全局队列;本地为空时尝试从其他 P 偷取一半任务,再检查全局队列、网络轮询器和定时器。系统调用阻塞时,M 可与 P 分离,让 P 绑定其他 M 继续运行。GOMAXPROCS 控制同时执行 Go 代码的 P 数量,而不是 goroutine 总数。


29. Java 中常见的锁有哪些?synchronized 的锁升级怎么理解?

来源:字节 Agent 开发一面,8 月 9 日;四维纵横三面,8 月 12 日

常见同步手段包括 synchronized、基于 AQS 的 ReentrantLock/读写锁、CAS 原子类、StampedLock。选型先看是否需要可中断、超时、公平性、多个条件队列或乐观读;普通互斥场景优先使用语义简单、自动释放的 synchronized

经典 HotSpot 实现会根据竞争程度使用偏向、轻量级和重量级形态,但偏向锁已经在新版本 JDK 中移除。回答时不要把“锁升级路径”背成永远不变的语言规范,它是 JVM 实现细节;核心是低竞争时尽量用 CAS 和栈上锁记录,高竞争时膨胀为监视器,避免持续自旋浪费 CPU。


30. 双重检查单例为什么必须配合 volatile

来源:四维纵横三面,8 月 12 日

public final class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        Singleton local = instance;
        if (local == null) {
            synchronized (Singleton.class) {
                local = instance;
                if (local == null) {
                    local = new Singleton();
                    instance = local;
                }
            }
        }
        return local;
    }
}

创建对象可抽象为分配内存、执行构造、发布引用。没有 volatile 时,发布引用可能被重排序到构造完成之前,其他线程便可能读到“非 null 但未完成初始化”的对象。volatile 同时提供可见性和禁止相关重排序的内存语义。更简洁的实现是静态内部类或枚举单例。


操作系统与 Linux

31. epoll 和 select/poll 的区别?

来源:杭州滴滴 CTO 面

维度 select/poll epoll
数据结构 线性扫描 fd 集合 红黑树 + 就绪链表
fd 上限 select 1024,poll 无硬限 无限制(系统内存为限)
通知方式 每次调用都遍历所有 fd 回调机制,只返回就绪的 fd
复杂度 O(n) O(1)(就绪事件)
集合维护 每次调用传入并扫描关注集合 内核持久维护关注集合,就绪事件单独返回

epoll 适合大量连接但活跃连接少的场景(典型:Web Server)。它仍需要通过系统调用把就绪事件返回用户态,并不是“mmap 零拷贝”;常说的红黑树 + 就绪链表也是 Linux 实现细节,不应背成 POSIX 接口保证。


32. 进程间通信(IPC)有哪些方式?

来源:华为暑期一面

方式 特点 适用场景
管道(Pipe) 单向、有亲缘关系 父子进程简单通信
命名管道(FIFO) 无亲缘关系也可用 不相关进程间
消息队列 有格式的消息、异步 结构化数据传递
共享内存 最快、需要同步机制 大数据量高频通信
信号量 同步/互斥 控制共享资源访问
Socket 跨机器、双向 网络通信、分布式系统

33. 为什么操作系统要区分用户态和内核态?什么时候会切换?

来源:虾皮 Data Infra 大数据平台研发一面,6 月 7 日;字节 AML / 火山方舟 AI Infra 一面,8 月 26 日

区分两种权限级别是为了最小权限和故障隔离:普通应用不能任意改页表、控制设备或读写内核地址,否则一个 Bug 就能破坏全机;内核集中管理 CPU、内存、文件和网络,通过稳定的系统调用接口提供受控服务。硬件特权级、页表权限和内核入口共同执行边界,单靠语言级检查不够。

系统调用、异常和硬件中断会进入内核态,例如 read、缺页异常、时钟或网卡中断;处理完成后可返回用户态。进入内核态需要切换特权级和栈、保存必要现场并执行安全检查,因此高频小系统调用会有可观测开销。模式切换不等于进程/线程上下文切换:同一线程执行系统调用可以只发生用户态/内核态切换,也可能因阻塞进一步触发调度。


34. 常见进程调度算法有哪些?

来源:虾皮 Data Infra 大数据平台研发一面,6 月 7 日

  • 先来先服务:实现简单,但长任务会阻塞短任务。
  • 最短作业优先:平均等待时间低,但需要估计运行时间,长任务可能饥饿。
  • 时间片轮转:响应公平,时间片过小会增加切换,过大则退化为先来先服务。
  • 优先级与多级反馈队列:兼顾交互任务和吞吐,需要老化机制防止低优先级饥饿。

Linux 的 CFS 通过虚拟运行时间近似公平分配 CPU;实时任务另有调度类。面试回答要区分教科书算法和实际内核实现。


35. 僵尸进程和孤儿进程分别是什么?

来源:虾皮 Data Infra 大数据平台研发一面,6 月 7 日

子进程退出后,父进程尚未调用 wait 回收退出状态时形成僵尸进程,它不再运行,但占用 PID 和进程表项。父进程先退出时,仍运行的子进程成为孤儿进程,会被 init/systemd 等收养并最终回收。大量僵尸进程应修复父进程的信号处理和 wait 逻辑,不能靠杀死已经退出的子进程解决。


36. Java 进程卡住如何排查?

来源:虾皮 Data Infra 大数据平台研发一面,6 月 7 日

先确认是 CPU 高、无响应、锁等待还是下游 IO 卡住。用 top -Hp/pidstat 找热点线程,jstackjcmd Thread.print 连续采样线程栈,观察死锁、阻塞点和线程池队列;再结合 GC 日志、堆/直接内存、连接池、网络超时和下游监控。单次线程快照可能误判,至少对比多次采样并关联请求 trace。


37. 进程、线程、协程有什么区别?

来源:百度后端一面,8 月 6 日;百度 Agent 开发二面,8 月 12 日

维度 进程 线程 协程
资源 独立地址空间和系统资源 共享进程资源,有独立栈和寄存器 在线程内共享资源,有独立执行状态
调度者 内核 内核 用户态运行时/事件循环
切换成本 最高 中等 最低
隔离性 最强 较弱 最弱

协程适合大量 IO 等待任务,因为 await 时主动让出执行权,少量线程就能管理大量连接;CPU 密集任务仍需要多进程、原生线程或任务下沉,协程本身不会让单核代码并行。


38. 一个进程的内存布局是什么?哪些区域容易溢出?

来源:百度后端一面,8 月 6 日;百度内容营销与广告一面,8 月 12 日

典型进程包含代码段、只读数据、已初始化数据段、BSS、堆、内存映射区和线程栈。堆通常向高地址增长,栈通常反向增长,动态库和 mmap 文件位于映射区,但具体布局受操作系统、架构和 ASLR 影响。

  • 栈溢出:递归过深或单帧局部数据过大。
  • 堆耗尽:泄漏、无界缓存或大对象持续分配。
  • 元空间/类加载区耗尽:动态生成类或类加载器泄漏。
  • 直接内存耗尽:NIO/本地库分配未受 Java 堆上限直接约束。

39. mallocfree 的内部原理是什么?

来源:百度后端一面,8 月 6 日

malloc 通常先从用户态分配器维护的空闲链表或 size class 中找合适块,小块复用 arena 中的内存,大块可能通过 mmap 单独映射;不足时再向内核申请页。free 不是简单“把内存清零”,而是把块归还分配器,尝试与相邻空闲块合并,满足条件时才把页归还操作系统。

主要问题包括内部/外部碎片、多线程锁竞争、double free、use-after-free。面试时可进一步说明 jemalloc/tcmalloc 通过线程缓存、分级空闲链表等方式降低竞争和碎片。


40. 进程间通信有哪些方式?共享内存为什么快、难点是什么?

来源:百度后端一面,8 月 6 日

IPC 包括管道/FIFO、消息队列、共享内存、信号、Socket、内存映射文件等。共享内存建立映射后,多个进程直接访问同一物理页,传输大块数据时避免反复在内核缓冲和用户缓冲之间复制,因此吞吐高。

但共享内存只解决“共享”,不解决“一致性”。还需要互斥锁、信号量、无锁协议或版本号处理并发访问,并设计对象布局、生命周期和崩溃恢复。小消息或跨机器通信通常更适合 Socket/消息队列。


41. selectpollepoll 的区别是什么?

来源:百度后端一面,8 月 6 日;字节后端社招二面,8 月 6 日

select 使用固定大小位图,每次调用都要复制集合并线性扫描;poll 改成数组,去掉固定上限,但仍需复制和 O(n) 扫描。epoll 通过内核维护关注集合和就绪队列,事件到达时回调加入就绪链表,应用只处理活跃 FD,适合连接多但同时活跃少的场景。

epoll 不是所有场景都更快:FD 很少或几乎全部活跃时,维护红黑树、回调和事件结构也有成本。还要能解释 LT/ET:边沿触发通常要求非阻塞 IO,并持续读写到 EAGAIN


网络、HTTP 与实时通信

42. TCP 三次握手为什么不是两次?四次挥手为什么不是三次?

来源:PDD 服务端一面 / 华为暑期一面

三次握手(不能两次)

  • 两次:客户端发 SYN,服务端回 SYN+ACK 就建立连接
  • 问题:如果客户端的旧 SYN(网络延迟)到达服务端,服务端会误建连接
  • 三次的意义:客户端确认服务端的 ACK 才算连接建立,防止历史连接初始化

四次挥手(不能三次)

  • 原因:TCP 全双工,关闭两个方向的数据流需要独立确认
  • 服务端收到 FIN 后可能还有数据要发,不能立即关闭,所以 ACK 和 FIN 分开发

43. 数据从网卡到 socket 缓冲区的流程?

来源:PDD 服务端一面

  1. 网卡收到数据帧 → DMA 拷贝到内核 Ring Buffer
  2. 网卡触发硬中断通知 CPU
  3. CPU 执行中断处理(上半部),调度软中断
  4. 软中断(NAPI)从 Ring Buffer 取数据包
  5. 经过网络协议栈逐层解封装(链路层→IP层→TCP层)
  6. 数据放入对应 socket 的接收缓冲区(sk_buff)
  7. 唤醒阻塞在该 socket 上的用户进程(或 epoll 回调)

44. 游戏网络包为什么常用二进制序列化?如何设计协议?

来源:灵犀互娱 Java 实习面经,6 月 4 日

二进制格式通常比文本体积小、解析快,适合高频位置和状态同步。包头至少包含魔数、版本、消息类型、长度和序列号,包体采用 Protobuf/FlatBuffers 等成熟格式,并设置最大长度和校验,防止粘包拆包错误及恶意内存分配。协议演进要遵循字段兼容规则,未知字段可跳过,发布时支持新旧版本并存。


45. HTTP 和 RPC 有什么区别?

来源:百度后端一面,7 月 30 日;拼多多 AI 全栈两轮技术面

HTTP 是协议,RPC 是远程调用抽象,两者不在同一分类层级。RPC 框架通常提供 IDL、代码生成、负载均衡、超时、重试和服务发现,并可使用 HTTP/2;HTTP API 更通用、易调试、浏览器生态友好。选型要看跨组织兼容、性能、治理和版本演进。


46. WebSocket 和 SSE 有什么区别?分别适合哪些通信场景?

来源:滴滴 AI Agent 后端面经,7 月 23 日;字节跳动火山引擎方舟 Managed Agent 一面,8 月 13 日

WebSocket 通常先通过 HTTP Upgrade 握手,再使用持久连接和独立帧协议,客户端与服务端都能主动发送文本或二进制消息。SSE 则是 Content-Type: text/event-stream 的长 HTTP 响应,只支持服务端向客户端推送 UTF-8 文本;客户端上行仍使用普通 HTTP 请求。

浏览器的 EventSource 原生支持断线重连,并可通过事件 idLast-Event-ID 请求续传,但服务端仍要保存可重放事件并处理重复消费。WebSocket 协议提供 Ping/Pong 控制帧,但浏览器 JavaScript 不暴露这些底层控制帧,业务保活、重连、鉴权续期、消息序号和断线补偿通常仍由应用层自行设计。

LLM Token 流、任务进度、通知和日志等单向事件流通常优先 SSE;聊天室、协同编辑、实时控制以及高频双向或二进制交互更适合 WebSocket。SSE 更容易复用 HTTP 网关和观测链路,但要关闭代理缓冲并协调空闲超时;WebSocket 则要确认代理升级、长连接路由和多节点扩展。两者都要处理背压、连接上限和慢消费者。


47. TCP 如何保证可靠?三次握手和四次挥手分别解决什么问题?

来源:百度后端一面,8 月 6 日;知乎后端一面,8 月 4 日;小红书数据库智能化二面,8 月 3 日

TCP 靠序列号、确认应答、校验和、超时重传、快速重传、滑动窗口、流量控制和拥塞控制共同提供可靠有序字节流。

三次握手让双方确认收发能力并同步初始序列号;两次无法让服务端确认客户端已收到自己的序列号,也更难排除历史连接请求。四次挥手是因为 TCP 全双工,两端发送方向要分别关闭,收到 FIN 的一端可能还有数据未发完,所以 ACK 和自己的 FIN 不一定能合并。


48. TCP 粘包是什么?怎么解决?

来源:百度后端一面,8 月 6 日

TCP 是字节流协议,没有消息边界。发送方多次 write 可能被合并,单次大消息也可能被拆分;这不是 TCP 出错,而是应用层协议没有定义帧。

常见方案:固定长度、特殊分隔符、消息头携带长度、或使用已有协议的 framing。生产中通常采用 magic/version/type/length/body/checksum,读取端先收固定头,再按长度循环读取完整 body,同时限制最大长度防止恶意包导致内存耗尽。


49. TIME_WAIT 为什么存在?数量过多怎么排查?

来源:百度后端一面,8 月 6 日

主动关闭方进入 TIME_WAIT,通常等待 2MSL:一是保证最后一个 ACK 丢失时还能重发,二是让旧连接的延迟报文从网络中消失,避免污染相同四元组的新连接。

数量过多通常说明服务频繁主动建立和关闭短连接。优先排查连接池、HTTP keep-alive、上游超时和负载均衡健康检查,而不是直接缩短内核等待时间。扩充可用端口、复用连接和调整架构通常比粗暴修改 TCP 参数更可靠。


50. 浏览器输入 URL 到页面返回,经历了什么?局域网里怎么找到目标 MAC?

来源:百度后端一面,8 月 6 日

主要链路是 URL 解析和缓存检查、DNS 解析、路由选择、ARP/NDP 获取下一跳链路地址、建立 TCP(HTTPS 还要 TLS)、发送 HTTP 请求、服务端经网关/应用/存储处理后返回,浏览器再解析渲染并加载子资源。

以 IPv4 为例,主机根据子网掩码判断目标是否同网段:同网段就 ARP 查询目标 IP 对应的 MAC;不同网段则查询默认网关的 MAC,把以太网帧交给网关。IP 目标地址端到端基本不变,二层源/目标 MAC 会在每一跳重新封装。


51. HTTP/2 相比 HTTP/1.1 做了什么优化?SSE 和普通 HTTP 有什么关系?

来源:百度后端一面,8 月 6 日;MetaApp 前端一面,8 月 12 日

HTTP/2 使用二进制分帧,在一个 TCP 连接上多路复用多个流,并提供 HPACK 头部压缩和流优先级等能力,解决 HTTP/1.1 应用层队头阻塞和大量重复头部问题。但它仍运行在单个 TCP 连接上,丢包时可能产生传输层队头阻塞,这也是 HTTP/3 改用 QUIC 的原因之一。

SSE 不是独立传输协议,而是长期保持的 HTTP 响应,服务端以 text/event-stream 持续推送事件。它适合服务端到浏览器的单向文本流;需要高频全双工交互、二进制或自定义消息协议时再考虑 WebSocket。


MySQL 与数据一致性

52. 数据库索引从各个角度介绍

来源:武汉风行在线 Agent 开发一面

物理存储

  • 聚簇索引:叶节点存完整行数据,一表一个
  • 非聚簇索引:叶节点存主键值,需回表查完整数据

底层数据结构

  • B+ 树索引:范围查询友好,InnoDB 默认
  • Hash 索引:等值查询 O(1),不支持范围,Memory 引擎用
  • 全文索引:倒排索引,适合文本搜索

业务逻辑

  • 主键索引、唯一索引、普通索引
  • 联合索引(最左前缀原则)
  • 覆盖索引(查询字段全在索引中,无需回表)

53. 慢查询如何定位和优化?

来源:武汉风行在线 Agent 开发一面

定位

  1. 开启慢查询日志(slow_query_log)
  2. 设置阈值(long_query_time)
  3. 用 mysqldumpslow 或 pt-query-digest 分析

EXPLAIN 核心字段

  • type:ALL(全表扫描)→ index → range → ref → const(越右越好)
  • key:实际使用的索引
  • rows:预估扫描行数
  • Extra:Using filesort / Using temporary / Using index

三个方向优化

  1. SQL 语句:避免 SELECT *、减少子查询、用 JOIN 替代 IN
  2. 索引:添加缺失索引、用联合索引覆盖高频查询、避免索引失效场景
  3. 数据库结构:分表分库、冷热分离、读写分离

54. 10 亿条数据,通过手机号后四位搜索用户,怎么设计?

来源:武汉风行在线 Agent 开发一面(业务面)

建表设计

  • 添加冗余字段:phone_suffix_4 存后四位
  • 反转字符串存储:便于 LIKE 'xxxx%' 走索引
  • 虚拟列(MySQL 5.7+):自动计算后四位建索引

存储架构

  • 分库分表:按手机号后四位做分区键(10000 个分区均匀分布)
  • 冷热分离:近期活跃用户热库,历史数据冷库
  • ES 辅助:全量数据同步到 ES,后四位作为 keyword 字段

查询方案

  • 带分区键查询 + MySQL 覆盖索引
  • 高频场景走 ES
  • 通过 Canal 监听 binlog 做数据同步

55. ClickHouse 和 MySQL 底层有什么区别?

来源:小红书 PE 后端一面

维度 MySQL ClickHouse
存储模型 行存储 列存储
适用场景 OLTP(事务处理) OLAP(分析查询)
写入模式 单行实时写入 批量追加写入
并发能力 高并发短查询 低并发长查询
事务支持 ACID 事务 无事务(最终一致)
压缩 一般 极高(列存 + 压缩算法)

ClickHouse 适合:日志分析、用户行为分析、指标看板等读多写少的分析场景。不适合高并发点查和事务场景。


56. MySQL redo log 是否一定安全?怎么保证?

来源:携程二面

  • redo log 默认配置 innodb_flush_log_at_trx_commit = 1:每次事务提交都 fsync 到磁盘 → 可保证不丢
  • 设为 0:每秒 fsync,宕机可能丢 1s 数据
  • 设为 2:写到 OS page cache,MySQL 挂不丢但机器断电会丢

要“一定安全”:sync_binlog = 1 + innodb_flush_log_at_trx_commit = 1(双 1 配置),牺牲性能换取零丢失。


57. 事务隔离级别?可重复读解决了什么?怎么实现的?

来源:小红书 PE 二面

级别 脏读 不可重复读 幻读
读未提交
读已提交(RC)
可重复读(RR) InnoDB 基本解决
串行化

可重复读比 RC 多解决:同一事务内多次读同一行结果一致(RC 下别人提交了你就能看到变化)。

实现:MVCC(多版本并发控制)

  • 每行有隐藏的 trx_id 和 roll_pointer
  • 事务开始时生成 ReadView(快照),只能看到小于自己 trx_id 的版本
  • RR 级别:整个事务复用同一个 ReadView → 可重复读
  • RC 级别:每次 SELECT 生成新 ReadView → 能看到最新提交

58. 常见索引失效场景?

来源:嘉立创一面

  1. 对索引列做函数/运算:WHERE YEAR(create_time) = 2026
  2. 隐式类型转换:WHERE phone = 13800138000(phone 是 varchar)
  3. LIKE 以 % 开头:WHERE name LIKE '%张'
  4. 联合索引不满足最左前缀
  5. OR 某一分支没有可用索引,导致优化器放弃索引合并
  6. 使用 !=NOT IN 后选择性太差,优化器认为全表扫描成本更低
  7. IS NULL / IS NOT NULL 是否走索引取决于可空性、数据分布和成本估算

“索引失效”不是语法黑名单。最终应通过 EXPLAIN ANALYZE 和真实数据分布确认访问路径。


59. 大表分页查询优化?

来源:嘉立创一面

问题:LIMIT 1000000, 10 实际扫描 100 万 + 10 行。

优化方案

  1. 游标分页WHERE id > last_id LIMIT 10(适合连续翻页)
  2. 延迟关联:先查主键 SELECT id FROM t LIMIT 1000000,10,再关联取数据
  3. 覆盖索引:如果只需要索引列,直接走索引不回表
  4. 业务限制:不允许翻到特别深的页(如最多看前 100 页)

60. undo log、redo log 和 binlog 分别做什么?

来源:字节 AI 应用开发一面,7 月 21 日

undo log 记录旧版本,用于事务回滚和 MVCC;redo log 是 InnoDB 的物理/页级重做日志,用 WAL 保证崩溃恢复;binlog 是 Server 层逻辑日志,用于复制和增量恢复。提交时通过两阶段提交协调 redo 与 binlog,避免主库恢复状态和复制日志不一致。


61. MySQL 两阶段提交解决什么问题?

来源:字节 AI 应用开发一面,7 月 21 日

事务先写 redo prepare,再写 binlog,最后把 redo 标记 commit。恢复时根据 redo 状态和对应 binlog 是否完整决定提交或回滚,从而维持引擎数据与 binlog 一致。它不是分布式业务的 2PC,而是 MySQL 内部协调两套日志的提交协议。


62. MVCC 如何实现可重复读?

来源:滴滴后端二面,7 月 28 日

记录包含事务 ID 和回滚指针,更新生成 undo 版本链;ReadView 根据活跃事务集合判断版本是否可见。InnoDB 可重复读下,同一事务的快照读通常复用 ReadView,因此多次看到一致版本;当前读仍需锁。RC 每条语句生成新的 ReadView,所以能看到其他事务后续提交。


63. 索引为什么快,哪些场景会失效?

来源:滴滴、字节、快手面经,7 月 23–30 日

B+ 树降低磁盘页访问并支持有序范围扫描。常见无法有效利用索引的情况包括对列做函数/运算、隐式类型转换、LIKE '%x'、不满足联合索引最左前缀、选择性太差导致优化器主动全表扫。IS NULL!=OR 不是绝对失效,必须结合数据分布和执行计划判断。


64. 两个请求并发更新同一行会怎样?

来源:百度后端一面,7 月 30 日;小红书/百度 Agent 开发实习一面

普通当前写会获取行级排他锁,后到事务等待、超时或遇到死锁回滚。如果没有正确锁定目标行,读改写可能发生丢失更新。可使用条件更新的乐观锁 WHERE version=?、原子 SQL、自增/累加表达式或串行化业务 key;应用必须处理冲突重试并设置上限。


65. 一条 SQL 在 MySQL 中如何执行?

来源:小红书数据库智能化一面,8 月 10 日

连接层完成认证和会话管理;Server 层解析 SQL、做语义检查和优化,生成执行计划;执行器按计划调用存储引擎接口;InnoDB 再通过索引、Buffer Pool、锁和 MVCC 读取或修改记录。写事务还会涉及 undo log、redo log 和 binlog,并通过两阶段提交维持 redo/binlog 一致性。

面试时不要只背组件名称,最好用 EXPLAIN 说明优化器会决定访问顺序、索引、连接算法和预估行数,慢查询定位也从实际执行计划开始。


66. CHARVARCHAR 和 JSON 怎么选?

来源:百度后端一面,8 月 6 日

CHAR(n) 定长,适合长度稳定且经常比较的值;VARCHAR(n) 按实际长度存储,适合长度变化大的字符串。选择时还要考虑字符集:n 是字符数,实际字节数受 utf8mb4 等编码影响。

JSON 类型适合结构有弹性、不是每个字段都参与关系约束的扩展属性。高频过滤、排序、关联字段应提升为普通列,或通过生成列/函数索引建立可用索引;不要把核心关系模型全部塞进 JSON 逃避表设计。


67. InnoDB 如何实现 ACID 和事务隔离?

来源:百度后端一面,8 月 6 日;百度内容营销与广告一面,8 月 12 日

  • 原子性:undo log 支持回滚。
  • 一致性:由原子性、隔离性、持久性以及业务约束共同保证。
  • 隔离性:锁 + MVCC;一致性读通过 ReadView 选择可见版本。
  • 持久性:redo log 的 WAL 机制,提交策略决定断电时的耐久程度。

InnoDB 默认可重复读。快照读主要靠 MVCC,当前读会加记录锁、间隙锁或临键锁。回答“隔离级别解决了什么”时,应同时区分 SQL 标准定义与 InnoDB 的具体实现。


68. B 树和 B+ 树有什么区别?MySQL 为什么用 B+ 树索引?

来源:百度后端一面,8 月 6 日

B 树的内部节点和叶子都可保存记录;B+ 树的内部节点只存键和子指针,完整记录集中在叶子,叶子还按顺序相连。同样页大小下,B+ 树内部节点能容纳更多键,树更矮、随机 IO 更少;叶子链表又适合范围扫描和排序。

InnoDB 聚簇索引叶子存整行,二级索引叶子存主键,因此通过二级索引查非覆盖列通常需要回表。联合索引遵循按索引定义顺序排列的最左前缀规律。


69. MySQL 主从复制和高可用怎么设计?

来源:百度后端一面、字节后端社招一面,8 月 6 日

主库提交事务并写 binlog,从库 IO 线程拉取日志写 relay log,SQL/applier 线程重放。并行复制可以提高回放速度,但复制通常存在延迟,因此读写分离要处理“写后立刻读”的一致性需求,例如关键读回主、GTID/位点等待或会话粘滞。

高可用还需要故障探测、选主、流量切换和脑裂防护。仅有一主一从不是完整高可用:要明确 RPO/RTO、半同步或组复制策略、fencing、自动化切换以及故障恢复后的数据校验。


70. 两个请求并发更新同一条记录,除了行锁还能怎么处理?

来源:百度秋招后端一面,7 月 30 日;小红书/百度 Agent 开发实习一面

悲观方案是在事务中 SELECT ... FOR UPDATE,适合冲突高、操作短的场景。乐观方案增加 version 字段,执行 UPDATE ... WHERE id=? AND version=?,影响行数为 0 时重读并重试;也可以用唯一约束、幂等键、串行消息队列或按业务键分区降低冲突。

关键是先说清一致性目标。金额扣减可以用原子条件更新 stock >= amount,不必先读后写;跨服务操作则需要幂等、事件表或 Saga,而不是幻想一个数据库行锁覆盖所有系统。


Redis 与缓存

71. Redis 为什么快?

来源:小红书 PE 后端一面 / 字节 AI 全栈二面

  1. 纯内存操作:数据全在内存,无磁盘 IO
  2. 单线程模型:无锁竞争、无上下文切换(6.0 后 IO 多线程,命令执行仍单线程)
  3. IO 多路复用:epoll 监听多个连接,非阻塞
  4. 高效数据结构:ziplist、skiplist、intset 等针对小数据量优化
  5. 简单协议:RESP 协议解析开销极低

72. Redis 大 Key 怎么解决?

来源:小红书 PE 后端一面

发现:redis-cli –bigkeys 扫描 / memory usage 命令 / 监控慢日志

解决

  • String 超大 → 拆分为多个小 key,或用 Hash 分片存储
  • Hash/Set 元素过多 → 按业务维度拆分(如按日期、用户分片)
  • List 过长 → 按时间窗口拆分为多个 list
  • 删除大 Key → 用 UNLINK(异步删除),避免 DEL 阻塞主线程

73. 缓存穿透、击穿、雪崩分别是什么?怎么解决?

来源:嘉立创一面 / 快手一面

问题 现象 解决方案
穿透 查不存在的数据,缓存和 DB 都 miss 布隆过滤器 / 缓存空值(短 TTL)
击穿 热点 key 过期瞬间大量请求打到 DB 互斥锁重建 / 逻辑过期(不设 TTL,后台更新)
雪崩 大量 key 同时过期 / 缓存宕机 TTL 加随机值 / 多级缓存 / 限流降级

74. Redis 过期删除策略和内存淘汰策略?

来源:嘉立创一面

过期删除

  • 惰性删除:访问时检查是否过期(省 CPU,但可能占内存)
  • 定期删除:每 100ms 随机抽取一批 key 检查过期(折中方案)

内存淘汰(达到 maxmemory 时)

  • noeviction:不淘汰,写入报错
  • allkeys-lru:全局 LRU
  • volatile-lru:仅对设了过期时间的 key 做 LRU
  • allkeys-random / volatile-random
  • volatile-ttl:淘汰 TTL 最短的
  • allkeys-lfu / volatile-lfu(Redis 4.0+)

75. Redis rehash 过程?

来源:PDD 服务端一面

  • Redis 使用两个哈希表(ht[0] 和 ht[1])
  • 渐进式 rehash:不是一次性迁移,而是每次 CRUD 操作时顺带迁移一个桶
  • 扩容触发条件:负载因子 > 1(无 BGSAVE)或 > 5(有 BGSAVE)
  • rehash 期间:新增写 ht[1],查询先 ht[0] 再 ht[1],直到迁移完成

76. 本地缓存未命中但 Redis 命中,怎么处理?

来源:PDD 服务端一面

标准缓存回填流程:

  1. 查本地缓存(Caffeine/Guava)→ miss
  2. 查 Redis → hit → 返回数据 + 异步回填本地缓存
  3. 设置本地缓存较短 TTL(如 30s),Redis 较长 TTL(如 30min)
  4. 注意:本地缓存容量有限,要配淘汰策略(LRU/LFU)

多级缓存一致性:更新 DB 后先删 Redis,再广播通知各节点清本地缓存(pub/sub 或消息队列)。


77. 缓存和数据库一致性策略有哪些?

来源:华为暑期一面 / 携程二面

策略 流程 优缺点
Cache Aside 读:先缓存→miss 查 DB→回填;写:先更新 DB→删缓存 最常用,但有短暂不一致窗口
Read/Write Through 缓存代理所有 DB 操作 一致性好,但缓存层复杂
Write Behind 异步批量刷 DB 性能最高,但可能丢数据

延迟双删:先删缓存→更新 DB→延迟 N ms 再删一次缓存。解决“更新 DB 前有读请求回填了旧值”的问题。N 通常 = 从库同步延迟 + 业务读耗时。


78. Redis 海量数据去重统计——HyperLogLog 和 BitMap?

来源:杭州滴滴 CTO 面

场景:统计每日 UV(独立访客数)

方案 原理 内存 精确度
Set 存所有 user_id 大(1亿用户 ~1.6GB) 精确
BitMap user_id 作为 bit 偏移量 固定(1亿用户 ~12MB) 精确(需要 id 是数字且连续)
HyperLogLog 概率算法,估算基数 极小(12KB 固定) 误差 ~0.81%

追问:id 比位图长怎么办?

  • 用 hash 函数将 id 映射到固定范围的整数
  • 或者用布隆过滤器做近似去重
  • 如果需要精确:分片 BitMap(按 id 前缀路由到不同 BitMap)

79. Redis 常见数据结构与适用场景是什么?

来源:滴滴 AI Agent 后端面经,7 月 23 日

String 用于缓存、计数和位图;Hash 存对象字段;List 适合顺序队列但可靠消息更宜用 Streams/MQ;Set 做去重和集合运算;ZSet 用分数排序,适合排行榜和延迟任务。选择时要看访问模式和元素规模,不能把一个超大集合塞进单 Key。


80. Redis 单线程为什么仍然快?阻塞会造成什么影响?

来源:滴滴 AI Agent 后端面经,7 月 23 日

内存访问、高效结构和 IO 多路复用让单线程事件循环可以高效处理大量短命令,并避免命令执行时的锁竞争。一旦执行大 Key 操作、复杂集合命令、同步删除或 fork/磁盘抖动,后续请求会排队,尾延迟迅速升高。治理依赖慢日志、拆 Key、渐进式操作、UNLINK 和实例隔离。


81. Redis ZSet 的底层结构是什么?

来源:滴滴 AI Agent 后端面经,7 月 23 日

小规模集合可使用紧凑编码;达到阈值后通常由哈希表和跳表组成。哈希表支持按成员 O(1) 找分数,跳表支持按分数排序和范围查询,平均 O(log n) 插入删除。两套结构保存相同成员,是用空间换取两类查询效率。


82. Redis 哨兵模式如何完成故障转移?

来源:滴滴 AI Agent 后端面经,7 月 23 日

Sentinel 周期探测实例,单个 Sentinel 判定主观下线后,与其他 Sentinel 协商形成客观下线,再选举领导者挑选从库晋升,并让其他从库复制新主。客户端要能发现拓扑变化并重连。Sentinel 提供高可用而不是数据零丢失,异步复制仍有故障窗口。


83. 大 Key 有什么风险?如何处理?

来源:滴滴 AI Agent 后端面经,7 月 23 日

风险包括单次命令阻塞、网络包过大、内存不均、迁移和过期删除抖动。用 --bigkeysMEMORY USAGE、慢日志和采样监控发现;按业务维度拆分集合、分页/渐进读取,删除时使用 UNLINK。拆分后必须同步设计路由和批量访问,避免把一次请求变成无界 fan-out。


84. 缓存穿透、击穿和雪崩如何区分?

来源:滴滴国际化后端二面、百度后端一面,7 月 28–30 日

穿透是查询不存在数据,使用校验、空值缓存和布隆过滤器;击穿是单个热点失效,大量请求同时回源,可用互斥重建、逻辑过期或热点永不过期;雪崩是大量 Key 同时失效或缓存集群故障,要随机化 TTL、多级缓存、限流降级和高可用。三者都要防止重试进一步放大流量。


85. 缓存与数据库如何保证最终一致?

来源:滴滴国际化、拼多多、百度后端面经,7 月 28–30 日

常用方案是先提交数据库,再删除缓存;删除失败进入重试队列或通过 binlog/CDC 补偿。读回填时可携带版本,防止旧请求覆盖新值;强一致要求更高时,可串行化热点写或直接绕过缓存。不要承诺缓存和数据库天然强一致,先明确业务允许的不一致窗口。


86. Redis 常见数据结构有哪些?String 和 Hash 底层如何实现?

来源:百度后端一面,8 月 6 日;快手测开一面,8 月 12 日

常用类型包括 String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、Stream 和地理位置索引。底层编码会根据数据规模动态选择:String 使用 SDS 或整数编码;Hash 在元素少且较小时使用紧凑的 listpack,超过阈值转为哈希表。

选型要从访问模式出发:唯一集合用 Set,排行榜用 Sorted Set,消息流用 Stream,用户对象少量字段读写可用 Hash。不要只背类型,还要说明大 Key、热 Key 和编码转换的内存影响。


87. 缓存失效后大量请求打到数据库,怎么解决?缓存一致性怎么保证?

来源:百度秋招后端一面,7 月 30 日;拼多多提前批一面,7 月 30 日

热点 Key 同时失效是缓存击穿。可用互斥重建/单飞、逻辑过期异步刷新、热点永不过期配合主动更新,并给 TTL 加随机抖动。还要用限流、熔断和数据库保护防止缓存故障拖垮下游。

一致性常用 Cache Aside:读未命中查库回填;写时先更新数据库,再删除缓存。删除失败要通过重试队列、binlog/CDC 或消息最终补偿。对强一致要求高的少数读可直接走数据库或版本校验,不能宣称普通缓存方案能提供无代价强一致。


消息队列与分布式系统

88. 分布式锁如何实现?

来源:武汉风行在线 Agent 开发一面

Redis 实现

  • SET key value NX EX(原子设置 + 过期时间)
  • Redisson 封装:看门狗机制(自动续期防止业务未完成锁过期)、可重入锁(计数器)
  • RedLock(多节点):半数以上节点加锁成功才算成功

注意事项

  • 必须设过期时间(防止死锁)
  • value 用唯一标识(释放时验证是自己加的锁)
  • 释放用 Lua 脚本保证原子性(判断 + 删除)

89. Kafka LAG 排查思路?

来源:小红书 PE 后端一面

LAG = 生产者最新 offset - 消费者已提交 offset。LAG 持续增大说明消费跟不上。

排查步骤

  1. 确认是否有消费者线程挂掉(consumer group 状态检查)
  2. 查看是否某个 partition 消费卡住(offset 不动)
  3. 分析单条消息处理耗时是否异常增大
  4. 检查是否触发了 rebalance(频繁 rebalance 导致消费停顿)
  5. 确认下游依赖是否变慢(DB/RPC 超时传导)

解决:扩 partition + 扩 consumer 实例 / 优化消费逻辑 / 异步处理 + 本地缓冲


90. Kafka 如何保证消息有序?

来源:杭州滴滴 CTO 面

  • 单 partition 内有序(offset 严格递增),跨 partition 无全局顺序
  • 保证业务有序的做法:同一业务 key 的消息发到同一 partition(通过 key hash)
  • 消费端单线程消费或按 key 分组有序消费
  • 注意:rebalance、重试、死信会打乱顺序,需要额外设计

91. MQ 消息丢失怎么办?如何保证不丢?

来源:嘉立创一面

三个环节防丢失:

  1. 生产者:开启 confirm 模式 / 事务消息,确认消息到达 Broker
  2. Broker:消息持久化到磁盘(RocketMQ 同步刷盘/异步刷盘 + 主从同步)
  3. 消费者:手动 ACK,处理完业务逻辑后再确认;失败进入重试队列

92. 消息重复消费怎么保证幂等?

来源:嘉立创一面

  • 数据库唯一键:利用业务唯一标识(订单号)做 INSERT 去重
  • Redis SETNX:消费前检查 key 是否存在
  • 状态机:只允许单向状态流转(待支付→已支付),重复消费时状态不匹配直接跳过
  • 乐观锁/版本号:UPDATE … WHERE version = x

93. 分布式锁除了 Redis 还有什么实现方式?

来源:杭州滴滴 CTO 面 / 华为暑期一面

方案 实现 优点 缺点
Redis SETNX + 过期时间 性能高 主从切换时可能丢锁
ZooKeeper 临时有序节点 + Watch 强一致,锁释放可靠 性能较低
MySQL 唯一键 INSERT / FOR UPDATE 实现简单 性能最差,不适合高并发
etcd 租约(Lease)+ Revision 强一致、高可用 部署运维复杂

94. Kafka 如何避免重复消费?

来源:滴滴国际化后端二面、拼多多后端一面,7 月 28–30 日

重复通常来自处理成功但 offset 未提交、消费者重平衡或生产者重试。端到端治理依赖业务幂等:唯一事件 ID + 数据库唯一键、状态机或幂等表;消费成功后再提交 offset。Kafka 事务能覆盖 Kafka 内部的 consume-transform-produce,但写外部数据库仍需 Outbox、幂等或事务协调。


95. 如何保证消息有序且不丢失?

来源:拼多多后端一面,7 月 30 日

同一业务 key 路由到同一 partition,分区内按顺序消费;失败重试若进入独立队列,需要设计业务序号或阻塞同 key 后续消息。防丢失要覆盖生产者确认与幂等、Broker 多副本与刷盘、消费者处理成功后提交,以及失败后的重试/死信/对账。不能只回答“设置 ack=all”。


96. MQ 积压如何排查?

来源:百度后端一面,7 月 30 日

先确认生产速率、消费速率和 lag 的时间趋势,再定位是否单分区热点、消费者异常、重平衡、慢消息或下游 DB/RPC 变慢。临时扩容消费者受 partition 数限制;长期要优化单条处理、批量与异步并发,并为失败消息隔离。扩容前先止住错误重试,否则只会放大下游压力。


97. 微服务注册、发现和负载均衡如何协作?

来源:滴滴 AI Agent 后端面经,7 月 23 日

服务实例启动后注册地址和元数据并通过心跳/租约保活;消费者从注册中心获取实例列表并监听变化,再由客户端或网关按轮询、权重、一致性哈希等策略选择实例。健康检查要区分进程存活和业务就绪,摘除需防抖,客户端还要处理缓存列表过期和注册中心不可用。


98. 消息队列如何处理顺序、重复和丢失?

来源:拼多多提前批一面,7 月 30 日;快手测开一面,8 月 12 日

  • 顺序:同一业务键路由到同一分区/队列,分区内单消费者或严格串行;全局顺序会牺牲吞吐。
  • 重复:MQ 通常只能提供至少一次,消费端用业务唯一键、去重表或状态机实现幂等。
  • 丢失:生产端确认和重试,Broker 持久化和副本,消费端处理成功后再提交 offset/ACK,失败进入重试与死信队列。

“Exactly Once”需要限定范围。消息系统内部的事务语义不等于数据库、缓存和外部 API 的跨系统严格一次,业务最终仍要依赖幂等与补偿。


99. MQ 突然积压,怎么定位?消息队列在架构中承担什么职责?

来源:百度秋招后端一面,7 月 30 日;快手测开一面,8 月 12 日

先比较生产速率和消费速率,再看消费者实例、分区分配、单条耗时、下游依赖、失败重试和大消息。短期可扩消费者、增加分区、批量处理或临时降级非核心逻辑,但消费者数量超过有效分区数不会继续提升并行度。

MQ 的核心职责是异步解耦、削峰填谷、广播事件和失败重试。代价是引入最终一致性、重复/乱序、积压和可观测性成本;同步且必须立即返回结果的短链路不应为了“架构高级”强行上 MQ。


系统设计与故障排查

100. 高并发限流有哪些算法?

来源:武汉风行在线 Agent 开发一面

算法 原理 优点 缺点
固定窗口 时间窗口内计数,超阈值拒绝 实现简单 窗口边界突发(两个窗口交界处可能 2x 流量)
滑动窗口 细粒度子窗口滚动统计 平滑,解决边界问题 内存占用稍大
漏桶 请求入桶,固定速率出桶 严格匀速,保护下游 无法应对突发流量
令牌桶 固定速率放令牌,请求需获取令牌 允许一定突发 实现稍复杂

分布式场景:用 Redis + Lua 实现(计数器/令牌桶),或用 Spring Cloud Gateway / Sentinel 等中间件。


101. 什么是聚合根?DDD 带来的收益和缺点?

来源:字节 AI 全栈研发二面

聚合根:一组相关对象的访问入口。外部只能通过聚合根操作内部实体,保证聚合内的业务规则一致性。例如 Order 是聚合根,OrderItem 只能通过 Order 操作。

收益

  • 业务逻辑内聚,不散落在 Service 层
  • 限界上下文隔离,团队可并行开发
  • 代码可读性高,领域模型即文档

缺点

  • 学习曲线陡峭,概念多(实体、值对象、领域事件、仓储…)
  • 简单 CRUD 场景过度设计
  • 聚合边界划分主观性强,不同人会有不同拆法
  • 跨聚合事务需要 Saga / 事件驱动,复杂度上升

102. 有状态服务如何改造成多节点部署?

来源:虾皮 Data Infra 大数据平台研发一面,6 月 7 日

先把会话、任务状态、文件和锁从单机内存剥离到具备明确一致性和恢复语义的共享存储,再让入口负载均衡。若协议需要连接粘性,应优先按稳定的用户或会话 ID 路由,不能依赖可能变化的客户端 IP;同时设计幂等请求、分布式锁/租约、故障转移、版本兼容和可观测性。共享 Redis 不是全部答案,它本身也要考虑分片、热点和故障。


103. 实时搜索联想和提交后搜索有什么区别?

来源:滴滴 AI Agent 后端面经,7 月 23 日

输入联想请求频率高、前缀短、容忍近似结果,需要防抖、取消旧请求、前缀索引/Trie、热点缓存和严格延迟预算;提交搜索强调相关性、过滤、排序和分页,可执行更重的召回与精排。两者可以复用搜索引擎,但索引、缓存和 SLA 不应完全相同。


104. 线上接口从 200ms 升到 5s 且 CPU 飙升,怎么排查?

来源:拼多多后端一面,7 月 30 日

先止损:限流、熔断、降级、暂停可疑发布并保护 DB/Redis/ES。再按变更时间线和 trace 判断延迟在哪一跳,检查 CPU 火焰图、线程池队列、GC、锁、慢 SQL、缓存命中、下游超时和重试量。定位后灰度修复,并通过容量压测、告警阈值和复盘防止重现。不要一看到 CPU 高就先扩容,重试风暴会让扩容也失效。


105. 短链系统如何设计?

来源:滴滴国际化后端二面,7 月 28 日

写入时生成全局唯一 ID,再做 Base62 编码得到短码,保存短码到长 URL 的映射;读取路径通过缓存加速并返回 301/302。需处理自定义短码冲突、恶意网址、安全扫描、过期、热点 Key、统计异步化和防枚举。随机码也可以,但要评估冲突重试和数据库唯一约束。


106. 接口平均延迟从 200ms 涨到 5s,同时 CPU 飙升,怎么排查?

来源:拼多多提前批一面,7 月 30 日;百度秋招后端一面,7 月 30 日

  1. 先确认影响范围、变更时间线和核心 SLI,必要时回滚、限流、熔断,先止损。
  2. 按 Trace 拆分网关、应用、DB、Redis、ES、RPC 各段耗时,区分排队、计算、锁等待、GC 和下游慢。
  3. CPU 高时抓火焰图/线程栈,检查死循环、序列化、正则、压缩、锁竞争和 GC;同时看容器 throttling,避免把 CPU 配额限制误判成机器满载。
  4. 用慢 SQL、连接池、缓存命中率、MQ lag 和下游错误率交叉验证根因;修复后回放流量并补监控、压测和回归用例。

排障顺序是止损 → 定界 → 定位 → 验证 → 防复发,不能一看到 CPU 高就直接扩容。


107. QPS 突增 10 倍,系统如何保证不崩?

来源:小红书数据库智能化一面,8 月 10 日;百度秋招后端一面,7 月 30 日

入口层做鉴权、令牌桶限流和请求优先级;无状态服务水平扩容;热点读使用多级缓存和请求合并;慢任务进入队列削峰;DB 通过索引、连接池上限、读写分离和批处理保护。每个下游都要有超时、熔断和隔离舱,避免一个依赖拖垮全部线程。

容量规划要从瓶颈反推,而不是只给应用加机器:确认单实例安全 QPS、缓存命中率、数据库连接和写入上限、队列可承受积压以及降级后仍需保留的核心功能。


108. Nginx 能不能做限流?常见策略是什么?

来源:小红书数据库智能化一面,8 月 10 日

可以。Nginx 常用 limit_req 基于漏桶思想限制请求速率,配置 burst 吸收短时突发,nodelay 决定突发请求是立即通过还是排队;limit_conn 控制并发连接数。Key 可按 IP、用户或 API 维度选择。

边缘限流能尽早挡住洪峰,但它看不到完整业务配额。生产系统通常采用网关粗限流 + 服务端按租户/接口精细限流,并把限流状态码、重试提示、白名单和监控一起设计。


109. 如何设计微信朋友圈或类似 Feed 系统?

来源:字节后端社招二面,8 月 6 日

先明确读写比、好友规模、时间线延迟和隐私规则。普通用户可采用写扩散:发布时把动态 ID 推入好友收件箱,读延迟低;大 V 采用读扩散,避免一次写入百万份;实际系统通常混合两者。

核心组件包括内容存储、关系图、Timeline 服务、缓存、异步 fan-out、排序和权限过滤。删除、拉黑和权限变更不能只依赖旧收件箱数据,读路径仍需做最终权限校验;还要考虑热点、游标分页、去重、补偿任务和多机房一致性。


前端与测试开发

110. Promise.allPromise.allSettled 有什么区别?

来源:MetaApp 前端一面,8 月 12 日

Promise.all 在任一任务拒绝时立即返回拒绝,适合结果缺一不可的并行依赖;其他 Promise 不会因此自动取消。Promise.allSettled 等全部任务结束,返回每项的 fulfilled/rejected 状态,适合批量任务、允许部分失败并需要汇总结果的场景。

需要真正取消网络请求时应配合 AbortController。大量任务还要限制并发,直接把上千请求交给 Promise.all 可能耗尽连接和内存。


111. Vue 3 响应式原理是什么?多次同步修改为什么通常只渲染一次?

来源:MetaApp 前端一面,8 月 12 日

Vue 3 用 Proxy 拦截对象属性访问和修改,通过 track 收集当前 effect 依赖,通过 trigger 通知相关 effect。ref.value 包装单值并可持有基本类型,reactive 返回对象代理;从 reactive 对象直接解构可能丢失响应式,需要 toRefs 等方式保持关联。

多次同步修改会触发多次调度,但组件更新任务会进入队列并按 job 去重,在微任务阶段统一 flush,所以通常只渲染一次。nextTick 等待的就是当前更新队列刷新完成后的时机,不是任意固定延迟。


112. 前端构建和运行时如何优化图片?FCP 应该怎么看?

来源:4399 前端一面,7 月 29 日;MetaApp 前端一面,8 月 12 日

构建阶段做尺寸裁剪、WebP/AVIF 转换、质量压缩、响应式 srcset 和内容哈希;运行时对首屏关键图预加载并设置明确尺寸,非首屏使用懒加载,静态资源走 CDN 和长期缓存。小图是否内联要看请求开销与缓存复用,不能统一转 base64。

FCP 表示首次绘制 DOM 内容的时间,目标应结合真实用户数据、设备和网络分位数,而不是只报一个实验室数字。优化路径包括减少阻塞 CSS/JS、服务端渲染或预渲染、字体策略、首屏资源优先级和后端 TTFB。


113. 自动化测试框架的基本原理是什么?人和 AI 的职责边界在哪里?

来源:快手测开一面,8 月 12 日

传统框架负责用例组织、驱动执行、断言、数据/环境管理、Mock、报告与失败重试。接口测试重点校验协议、状态码、Schema、业务副作用和幂等;UI 自动化通过稳定定位符和页面对象封装操作,并保留截图、日志、网络和 Trace 证据。

AI 适合从需求生成候选用例、补边界、维护脆弱定位符、聚类失败和辅助定位根因;确定性执行、关键断言、权限与副作用控制仍应由代码和人负责。高风险发布不能以“模型觉得通过”作为唯一门禁。


Python 与语言设计

114. Python 函数参数有哪些类型?*args**kwargs 分别做什么?

来源:阿里云 AI 全栈开发一面

Python 参数包括仅限位置参数(/ 之前)、位置或关键字参数、可变位置参数 *args、仅限关键字参数和可变关键字参数 **kwargs。调用时,*iterable 把可迭代对象展开为位置参数,**mapping 把字符串键映射展开为关键字参数;重复绑定、缺少必需参数或出现未接收的关键字都会报错。

默认值在函数定义时求值,而不是每次调用时求值,因此不要用可变对象作默认值。接口设计应把稳定、必需的参数显式写出,避免用 *args**kwargs 隐藏契约。


115. Python 的鸭子类型是什么?如何兼顾灵活性和可维护性?

来源:阿里云 AI 全栈开发一面

鸭子类型关注对象是否提供所需行为,而不是它是否继承某个指定类。它降低了模块耦合,便于替换实现、测试替身和接入第三方对象,但错误往往到运行时才暴露,接口也可能不易发现。

工程上可用清晰的最小协议、类型注解和 typing.Protocol 描述能力边界,在外部输入处做校验,并用契约测试覆盖不同实现。不要把鸭子类型理解为完全不需要类型设计。


116. Python 的迭代器和生成器有什么区别?底层如何保持执行状态?

来源:阿里云 AI 全栈开发一面

可迭代对象通过 __iter__() 返回迭代器,迭代器用 __next__() 逐项产出并在结束时抛出 StopIteration。生成器是由含 yield 的函数或生成器表达式创建的一类迭代器;每次 yield 会挂起执行,并保存指令位置、局部变量和异常处理状态,下次迭代再恢复。

两者都适合惰性处理数据,但迭代器通常只能单次消费,生成器也不会自动缓存已产出的值。处理文件、游标或网络流时还要明确关闭和异常清理路径,不能只依赖垃圾回收。


117. 托管语言不暴露裸指针和指针算术,有哪些收益与代价?

来源:视频 b 二面

不允许任意地址读写和指针算术,可以减少越界访问、悬空指针、重复释放等内存安全问题,也让 GC 能移动对象并保持跨平台对象模型。代价是开发者难以精确控制布局、生命周期和零拷贝路径,FFI、设备内存及高性能系统代码需要额外抽象,GC 还可能带来延迟波动。

这不代表语言内部没有引用或地址。成熟运行时通常提供数组切片、安全句柄和受控的 unsafe/FFI 边界;工程原则是把不安全代码压缩到少量、可审计且有基准测试的模块中。


AI Infra、网络与协作工具

118. NPU 和 GPU 在硬件架构与编程模型上有什么区别?

来源:百度 AI Infra 一面

GPU 是面向图形与通用并行计算的可编程处理器,生态成熟、算子覆盖广,适合训练和包含动态控制流的混合负载。NPU 通常围绕矩阵乘、卷积和量化算子设计数据流、片上存储及专用执行单元,单位功耗吞吐可能更高,但算子支持、动态形状、编译器和调试能力更依赖具体平台。

选型不能只比较峰值 FLOPS/TOPS。还要用真实模型衡量算子回退、编译时间、显存/片上内存、数据搬运、精度、批量大小、端到端延迟和部署生态。


119. 扫码登录的完整流程是什么?如何防止二维码被重放?

来源:快手 Agent 开发一面

网页先向服务端申请短期、随机、只使用一次的二维码 ID,并把它与当前浏览器会话绑定。已登录的移动端扫码后只上报该 ID,服务端先把状态改为已扫描;用户在手机上确认后,网页通过轮询、SSE 或 WebSocket 收到结果,再原子消费该 ID 并建立自己的登录会话。

二维码中不应直接包含长期凭据。系统还要设置过期时间、一次性消费、设备和业务信息确认、速率限制及审计,校验请求来源并防止并发确认。已过期、已取消或已消费的 ID 必须拒绝再次兑换。


120. HTTP Keep-Alive 和 HTTP 连接池是什么关系?

来源:信服 Agent 开发一面

HTTP Keep-Alive 是协议层允许多个请求复用同一 TCP 连接的能力;连接池是客户端对一组可复用连接的资源管理,包括按目标地址分池、最大连接数、空闲队列、存活检查、空闲超时和最大寿命。只有 Keep-Alive 而没有合理的池化,仍可能频繁建连或产生无界连接。

HTTP/1.1 中一条连接通常同时承载一个在途请求,连接池靠多条连接提供并发;HTTP/2 可以在一条连接上多路复用多个流。池的超时应与服务端、代理和负载均衡器协调,否则复用到已被对端关闭的连接会产生间歇性失败。


121. TCP/HTTP 抓包该选什么工具?HTTPS 内容在什么条件下可以解密?

来源:视频 b 二面

链路和传输层问题可用 tcpdump 抓取、Wireshark 分析;浏览器请求先看 DevTools,需要观察或改写应用层流量时可在授权测试环境使用 Charles、Fiddler 或 mitmproxy。先按问题选择观测层级,不要为了看一个 HTTP Header 就抓整机流量。

HTTPS 抓包默认只能看到地址、握手和流量特征,看不到明文。受控环境可使用客户端导出的 TLS 会话密钥,或让测试设备信任调试代理的 CA;证书固定、mTLS、QUIC 和不支持导出密钥的客户端会限制这种方式。不得在未授权设备上安装根证书,也不应通过收集生产私钥来绕过安全边界。


122. git mergegit rebase 有什么区别?项目中如何选择?

来源:视频 b 二面

merge 保留原有提交和分叉关系,并用合并提交连接两条历史;rebase 把一组提交重新播放到新基线,提交 ID 会改变,但历史更线性。两者最终都能整合代码,区别主要在历史语义和协作风险。

尚未共享的个人分支可在合入前 rebase,便于整理提交;已经发布、多人依赖或受保护的分支通常用 merge,避免改写他人历史。确需更新共享分支时要先协调,并使用 --force-with-lease 而不是无条件强推;复杂 rebase 还要逐提交解决冲突并重新测试。


前端、搜索与业务一致性

123. 虚拟列表的基本原理是什么?实现时有哪些容易忽略的问题?

来源:税友开发实习一面

虚拟列表只渲染视口附近的元素,用占位容器保持总滚动高度,再通过偏移量把可见节点放到正确位置;上下多渲染少量 overscan 可降低快速滚动时的白屏。固定高度列表可直接按索引计算,动态高度列表需要测量、缓存并在高度变化后修正偏移。

实现时要保持稳定 key,处理滚动、窗口缩放、焦点和键盘访问,并避免频繁测量触发布局抖动。虚拟化只减少 DOM 和渲染成本,不会自动减少后端数据量,仍要配合分页、取消旧请求和缓存。


124. SSR 和 CSR 有什么区别?如何选择渲染方式?

来源:税友开发实习一面

SSR 在服务端生成可直接展示的 HTML,通常有利于首屏和搜索引擎抓取,但会增加服务端计算、缓存及部署复杂度,交互前还要完成 hydration。CSR 由浏览器下载 JavaScript、获取数据并渲染,静态托管和前后端解耦更简单,但弱网或低端设备上的首屏可能更慢。

选择应看 SEO、首屏 SLA、交互复杂度、个性化程度和基础设施,而不是全站二选一。常见方案是营销和内容页使用 SSR/SSG,登录后的高交互页面使用 CSR;SSR 还要避免服务端与客户端状态不一致导致 hydration mismatch,并安全序列化初始数据。


125. 页面出现卡顿时,应该如何定位和优化?

来源:OPPO AI 全栈开发一面

先固定设备、网络、操作路径和性能指标,区分加载慢、交互响应慢、滚动掉帧还是内存持续增长。使用浏览器 Performance 面板查看长任务、脚本、样式计算、布局和绘制,结合 Network、Memory、FPS、Web Vitals 以及组件 profiler 定位真正的主线程或资源瓶颈。

优化要对应证据:拆分长任务或移到 Worker,批量 DOM 读写避免强制同步布局,对高频事件节流,长列表虚拟化,减少无效渲染并优化图片和缓存。完成后用相同场景复测,并关注真实用户的 P75 INP、LCP、CLS,而不是只比较一次本机录屏。


126. Elasticsearch 的查询流程是什么?倒排索引如何参与检索?

来源:小红书开发实习一面

协调节点解析并重写查询,把请求路由到相关分片;每个分片在 query 阶段利用词典和倒排表找到包含 term 的文档,执行过滤、评分并返回局部 Top K;协调节点归并排序后,再进入 fetch 阶段从命中分片取回 _source 或所需字段。

精确过滤尽量放在 filter 上下文以避免无意义评分并利用缓存,排序和聚合通常依赖 doc values。排查慢查询还要关注分词和映射、深分页、脚本、聚合基数、分片扇出及 fetch 数据量,不能把所有延迟都归因于倒排索引。


127. Elasticsearch 有 12 个分片、QPS 只有 1 仍然很慢,如何排查并重建索引?

来源:小红书开发实习一面

先用 slow log、Profile API 和分段耗时确认慢在协调、query 还是 fetch,再检查查询 DSL、映射、分片数据倾斜、冷缓存、磁盘、合并、刷新、GC 和线程池队列。低 QPS 不代表低成本;过多小分片会让每次请求扇出到更多 Lucene 实例,增加协调、文件句柄和堆开销。

重建时创建具有正确 mapping、分片数和刷新策略的新索引,通过 reindex 或 CDC 回放数据,校验数量、关键查询和抽样哈希后原子切换 alias。迁移期间要处理增量写入并保留回滚窗口,不能直接删除旧索引,也不能期待原地修改主分片数解决所有问题。


128. DDD 和 MVC 有什么区别?为什么 Domain 层不应依赖 Infrastructure 层?

来源:小红书开发实习一面

MVC 主要组织交互和展示职责,DDD 关注复杂业务的领域建模、限界上下文、聚合及不变量,两者不在同一抽象层级,也可以同时使用。常见分层是接口/展示层、应用层、领域层和基础设施层:应用层编排用例,领域层表达业务规则,基础设施层实现数据库、消息和外部服务适配。

Domain 通过自己定义的仓储等接口依赖抽象,Infrastructure 反向实现这些接口,使核心规则可独立测试和演进。充血模型把行为和不变量放进领域对象;简单 CRUD 若没有复杂规则,强行套完整 DDD 只会增加映射和调用层级。


129. MySQL 同步 Elasticsearch 时,如何防止订单状态被乱序事件回退?

来源:小红书开发实习一面

数据库事务提交后通过 outbox 或 binlog CDC 发布变更,每条订单事件携带单调递增的版本号。消费者按订单 ID 分区以保持局部有序,并在写 ES 时使用外部版本或条件更新,只接受比当前版本新的事件;重复消费要幂等,失败进入有界重试和死信队列。

不需要为了一个订单的顺序牺牲全局并行度,也不应每条事件都回查 MySQL。可用 singleflight、短期缓存或批量合并保留每个订单的最新版本,并用定期对账修复漏事件;完成态能否再变化应由显式状态机和合法迁移规则决定,而不是只比较状态字符串。


分布式执行与稳定性

130. ZooKeeper 的 QPS 应该如何压测?常见瓶颈在哪里?

来源:小舒一面

ZooKeeper 没有脱离环境的固定 QPS。结果取决于读写比例、数据大小、watcher 数量、会话创建、客户端并发、磁盘同步、网络时延、集群规模和一致性要求。压测应使用与生产接近的读写及 watch 模型,预热后逐步增加并发,同时记录吞吐、P50/P95/P99、错误率、未完成请求和节点资源。

写请求要经过 Leader 排序、事务日志落盘和多数派确认,常受磁盘及 quorum 时延限制;读请求通常可由本地节点处理,但大对象、watch 风暴、会话抖动和请求队列同样会拖慢系统。回答一个背来的 QPS 数字没有意义,应解释测试条件、饱和点和延迟拐点。


131. 特征依赖形成 DAG 时,如何处理超时、降级、缓存和请求去重?

来源:小舒一面

先把特征计算建模为有向无环图,显式标记依赖、关键性和每个节点的预算。调度器并行执行互不依赖的节点并传播全局 deadline;关键前置失败时取消无意义的下游任务,非关键特征超时则使用默认值、最近一次成功值或降级模型,同时把降级信息传给最终结果。

缓存键必须包含实体、特征版本和影响结果的上下文,避免跨用户或跨版本误用。相同在途计算可用 singleflight 合并,已完成结果按新鲜度和业务容忍度复用;重试只用于幂等、可恢复错误且不得突破剩余预算。监控应展示关键路径、节点命中率、去重率、超时和降级比例,便于区分单节点慢与 DAG 放大效应。


身份认证与会话

132. Cookie、Session 和 JWT 有什么区别?JWT 的结构和验签流程是什么?

来源:字节跳动火山引擎方舟 Managed Agent 一面,8 月 13 日

Cookie 是浏览器按域、路径等规则保存并随请求发送的小型键值载体,不等于认证方案;Session 是服务端保存的会话状态,客户端通常只在 Cookie 中携带随机 Session ID;JWT 是一种 Token 格式,既可以放在 Authorization 请求头,也可以放进 Cookie。因此三者不在同一层级,Session ID + CookieJWT + Cookie 都成立。Session 容易即时撤销,但分布式部署需要共享会话;JWT 便于多个服务本地验证,但体积更大,令牌中的权限也可能过时。

常见的签名 JWT 由 header.payload.signature 三段组成。Header 声明算法和 kid,Payload 保存 subissaudexpnbfjti 等 claims;前两段只是 Base64URL 编码,并未加密。Signature 对前两段的原始编码结果签名:HMAC 使用共享密钥,RSA、ECDSA 等算法使用私钥签发、公钥验证。

服务端不能直接相信解码后的 Payload,而应固定允许的算法,按受信任的 kid 选择密钥,验证签名后再校验 expnbfissaud、主体和令牌用途。验签通过只说明来源可信且内容未被篡改,不代表当前请求一定有业务权限;授权规则仍要单独检查。

JWT 所谓无状态,是因为身份声明和完整性证明都随 Token 携带,服务端验证 access token 时不必像随机 Session ID 那样查询会话记录,并不表示整个认证系统没有状态。生产中通常使用短寿命 access token 和可轮换、可撤销的 refresh token;需要立即失效时,可查询 jti denylist、会话版本或最后登出时间,代价是重新引入状态。计划内轮换应通过 kid 并行发布新旧验证密钥,并将旧验证密钥保留到已签发 Token 过期;密钥泄漏时则应立即下线旧密钥,并通过拒绝列表、会话版本或强制重新登录处置受影响令牌。

签名不等于加密,Payload 不应存放秘密;服务端不得接受 alg=none,也不能让 Token 自己决定允许的算法。Token 放在 localStorage 容易被 XSS 读取;放在 HttpOnlySecureSameSite Cookie 中可以降低脚本窃取风险,但仍要根据跨站场景防范 CSRF。JWT 本身并不天然比服务端 Session 更安全。


近期新增:语言、云原生与稳定性

133. JavaScript 的事件循环是怎么工作的?

来源:拼多多 AI 全栈两轮技术面,8 月 23 日

JavaScript 主线程执行当前调用栈;异步 API 由宿主环境处理,完成后把回调放入任务队列。一次宏任务结束且调用栈清空后,运行时会清空微任务队列,再进入渲染和下一轮宏任务。Promise.thenqueueMicrotask 属于微任务,setTimeout、I/O 回调通常属于宏任务;Node.js 还按 timers、poll、check 等阶段推进,并有 process.nextTick 等更高优先队列。持续追加微任务会让定时器和渲染饥饿,不能只背“微任务先于宏任务”。


134. TypeScript 中 interfacetype 应该怎么选?

来源:拼多多 AI 全栈两轮技术面,8 月 23 日

两者都能描述对象并支持扩展。interface 支持声明合并,适合公开、可扩展的对象契约和类实现;type 能表达联合、交叉、条件类型、元组及映射类型,组合能力更强。工程上对稳定对象 API 可优先 interface,对联合状态和类型运算用 type。选择应服务于可读性和团队规范,不应声称二者存在普遍的性能差异。


135. Node.js 如何读取大文件,避免一次性占满内存?

来源:拼多多 AI 全栈两轮技术面,8 月 23 日

使用 createReadStream 分块读取,并通过 pipepipeline 把数据传给转换和写入流。背压会在下游缓冲区达到高水位时暂停上游,待 drain 后继续,避免生产速度长期高于消费速度。还要处理流错误、取消、文件描述符关闭和不完整输出;按行解析时需保留跨 chunk 的残片。readFile 适合可控的小文件,不适合把未知体积内容整体放入堆。


136. kubectl create pod 后,Kubernetes 各组件如何协作?

来源:字节社招一面,8 月 23 日

kubectl 向 API Server 提交对象;API Server 完成认证、授权、准入和 schema 校验后把期望状态写入 etcd。Scheduler 监听未绑定 Pod,经过过滤和打分选择节点并写回绑定结果;目标节点的 kubelet 发现任务后通过 CRI 请求容器运行时拉镜像、创建 sandbox 和容器,通过 CNI 配置网络、CSI 挂载存储。kubelet 持续上报状态,控制器负责副本和故障收敛。API Server 是控制面的入口,不应描述为各组件直接读写 etcd。


137. Pod、Service 和 Ingress 在请求流量路径中分别承担什么角色?

来源:字节社招一面,8 月 23 日

Pod 是实际运行工作负载的最小调度单元,IP 会随重建变化;Service 提供稳定虚拟地址和服务发现,并把流量转发到匹配标签的健康 Pod;Ingress 描述集群入口的 HTTP/HTTPS 路由规则,必须由 Ingress Controller 实现。典型外部链路是负载均衡器或节点入口到 Ingress Controller,再到 Service 和 Pod。网络策略、就绪探针、会话保持及 CNI 实现会影响实际路径,不能把 Ingress 当成自动存在的代理进程。


138. 多租户系统如何同时实现数据隔离和资源隔离?

来源:杭州某小厂实习面经,8 月 19 日

数据层可按风险和规模选择独立库、独立 schema 或共享表加 tenant_id。共享表不能只依赖开发者手写 WHERE tenant_id,应在认证后形成不可伪造的租户上下文,由 ORM 全局过滤、数据库行级安全和唯一索引共同约束;缓存、对象存储、消息和搜索索引也必须把租户纳入命名空间。资源层再设置连接池、线程池、队列、QPS、存储和成本配额,防止热点租户拖垮他人。审计日志应记录租户、操作者和请求链路。


139. Python 装饰器的原理是什么?常见工程用途有哪些?

来源:燧原软件解决方案一面,8 月 21 日

函数是对象,装饰器接收函数并返回新的可调用对象;@decorator 等价于定义后执行 func = decorator(func)。带参数装饰器会再增加一层闭包。工程中常用于日志、指标、鉴权、缓存、重试和事务边界。实现时用 functools.wraps 保留函数名、文档和签名,并谨慎处理同步/异步函数、异常语义和共享可变状态,避免装饰器悄悄改变原函数契约。


140. mmap 是什么?适合哪些文件和进程通信场景?

来源:拼多多 AI Agent 岗技术面,8 月 19 日

mmap 把文件或匿名内存映射到进程虚拟地址空间,访问时由缺页机制按需装入页面,省去显式 read/write 的一层用户缓冲区复制,也便于随机访问和多进程共享。它适合大文件随机读取、索引和共享内存,但不是无成本:缺页、脏页回写、地址空间、文件截断和一致性都要处理;小文件顺序读取未必比缓冲 I/O 更快。多进程写共享映射仍需锁或无锁协议。


141. ZGC 相比 G1/CMS 的核心设计和适用场景是什么?

来源:途游 Agent 二面,8 月 18 日

ZGC 以超低停顿为目标,把标记、重定位和引用处理的大部分工作与应用并发执行,并通过染色指针和加载屏障维护对象转移期间的正确访问。它适合大堆、对尾延迟敏感的服务,停顿通常不随堆大小线性增长;代价是并发 GC 线程、屏障和额外内存带来的吞吐开销。选择前应基于延迟 SLO、堆规模、分配速率和 CPU 余量压测,而不是看到低停顿就无条件替换 G1。


142. 为什么 CSRF 请求能自动携带 Cookie?应该如何防御?

来源:字节抖音 Agent 一面,8 月 18 日

浏览器是否发送 Cookie 主要依据目标域、路径、Secure 和 SameSite 等属性,而不是依据发起页面是否可信。攻击页面可以诱导浏览器向已登录站点发请求,浏览器便可能附带该站点 Cookie。防御应组合使用 SameSite=Lax/Strict、不可预测的 CSRF Token、Origin/Referer 校验和对敏感操作的重新认证;同时避免用 GET 修改状态。CORS 主要限制脚本读取响应,不等价于 CSRF 防护。


143. MySQL B+ 树索引页分裂会造成什么影响?如何降低写放大?

来源:多益网络 AI 应用开发一面,8 月 17 日

目标页空间不足时,InnoDB 需要分配新页、移动部分记录并更新父节点;分裂会增加随机 I/O、redo、页碎片和缓存扰动,连续发生还可能向上级传播。随机主键和在中间位置频繁插入更容易触发分裂。可优先使用趋势递增且不过宽的主键,控制索引数量和字段宽度,批量排序写入,并根据真实碎片和空间利用率选择重建索引。盲目把填充率压得很低会浪费缓存,仍需按写入模式权衡。


144. Next-Key Lock 如何防止幻读?为什么仍可能引发死锁?

来源:多益网络 AI 应用开发一面,8 月 17 日

InnoDB 在可重复读下对索引记录及其前方间隙加锁,阻止其他事务在查询范围内插入新记录,从而保护当前读的范围结果。锁定范围取决于索引和查询条件;缺少合适索引可能扫描并锁住更大区间。两个事务若以不同顺序锁多个记录或间隙,或插入意向锁与范围锁形成等待环,仍会死锁。治理要统一访问顺序、缩小事务和扫描范围、建立合适索引,并让应用捕获死锁后有界重试。


145. 数据库事务已提交,但消息发送失败,如何保证任务不丢失?

来源:多益网络 AI 应用开发一面,8 月 17 日

常用 Transactional Outbox:业务更新和待发布事件在同一个本地数据库事务中提交,后台发布器轮询或由 CDC 读取 outbox,发送成功后标记状态。发布器崩溃可能导致重复发送,因此消费者必须按 event ID 幂等;失败需要有界重试、告警和死信处理。直接“先写库再发 MQ”存在崩溃窗口,先发消息再提交又可能让消费者看到不存在的业务状态;分布式事务只在确有强一致需求且基础设施支持时采用。


146. Java 服务 Full GC 频繁但堆使用量并不高,如何排查?

来源:阿里 Agent 开发一面,8 月 17 日

先从 GC 日志和 JFR 确认触发原因、回收前后占用、停顿阶段和分配速率,而不是只看监控截图。低堆占用仍可能由元空间耗尽、直接内存压力、显式 System.gc()、Humongous 对象、晋升失败、类加载器泄漏、JNI 或 GC 参数触发。再结合 native memory tracking、类直方图、线程栈和容器内存限制定位堆外来源。修复根因后再调堆和收集器;单纯扩大 -Xmx 对元空间或直接内存问题无效。


近期新增:工程平台与语言运行时

147. Go context 如何传播取消、超时和请求级数据?

来源:视频一面,8 月 9 日

context 形成父子树,父节点取消或 deadline 到期会关闭所有后代的 Done;调用链应把 ctx 作为首个参数显式传递,I/O、RPC 和 goroutine 同时监听取消信号。WithValue 只放请求 ID 等少量请求级元数据,不用于业务参数;创建的 cancel 必须调用,避免定时器和子节点泄漏。后台任务若需脱离请求,应显式创建新生命周期并复制必要元数据。


148. Go 定时器和 Ticker 的运行时机制与常见泄漏是什么?

来源:视频一面,8 月 9 日

运行时维护按时间排序的定时器结构,由调度器和网络轮询共同唤醒到期任务。Timer 单次触发,Ticker 周期触发;复用时正确处理 Stop/Reset 和通道中可能残留的事件。长期循环应停止 Ticker,不能反复 time.After 制造大量短命定时器;回调耗时超过周期时还要明确跳过、串行还是并发策略。


149. 单个 Pod CPU 或内存异常时,如何定位到进程和代码?

来源:视频一面,8 月 9 日

先确认 request/limit、throttling、OOMKilled、重启和节点压力,再比较同版本 Pod 判断是流量倾斜还是实例异常。进入容器查看进程、线程、文件描述符和 cgroup 指标;Java 用 JFR/jstack/heap dump,Go 用 pprof,通用场景用 top、pidstat、perf。结合请求 trace、GC、分配率和发布变更定位根因,避免只重启或盲目扩容。


150. OAuth 2.0 授权码流程如何工作?PKCE、state 和 redirect URI 分别防什么?

来源:小红书数据库智能化实习一面,8 月 10 日

客户端把用户引导到授权端点,携带 client、固定 redirect URI、scope、state 和 PKCE challenge;用户授权后返回短寿命 code,客户端后端用 code、client 身份和 verifier 换 Token。state 绑定发起会话防 CSRF,PKCE 防 code 被截获后换 Token,redirect URI 必须精确白名单防跳转劫持。Access Token 短寿命,Refresh Token 轮换并可撤销;OAuth 是授权框架,不等于登录协议,登录通常由 OIDC 补充身份声明。


151. CI/CD 流水线应包含哪些阶段和质量门禁?

来源:松延动力科技测试实习面试,8 月 12 日;米哈游开发一面,8 月 12 日

典型流程是依赖锁定与构建、静态检查、单元测试、制品签名、集成/安全测试、部署预发布、冒烟与回归、审批/自动发布。制品应一次构建、多环境晋级,不能每个环境重新编译。门禁按风险设置,失败默认阻断;不稳定测试先隔离并告警,不能静默重跑到通过。发布绑定版本、配置、迁移和回滚目标,并用金丝雀指标决定扩量。


152. 线上真实流量如何安全录制、脱敏和回放?

来源:益善一面,8 月 11 日

在网关或服务边界采样请求及必要上下文,删除凭据、PII 和不可复现字段,使用稳定映射保持关联关系。回放必须进入隔离影子环境,写操作替换为 Mock、影子库或强制只读,并重写时间、ID 和外部依赖。按原始节奏或受控倍速施压,对比响应、状态和副作用;记录数据版本和覆盖范围,不能把生产请求直接重放到真实下游。


153. Python 浮点数为什么不精确?金额计算应该怎么做?

来源:益善一面,8 月 11 日

二进制浮点无法有限表示许多十进制小数,运算还会累积舍入误差,因此 0.1 + 0.2 != 0.3。比较使用容差而非直接相等;金额优先用最小货币单位整数或 Decimal,并明确舍入模式和精度。字符串转换为 Decimal,避免先经过 float;数据库、JSON 和跨服务协议也应统一金额单位。


154. 大文件分片上传和断点续传如何设计?

来源:华为通用软件开发一面,8 月 12 日

初始化任务后返回 upload ID、分片大小和已存在分片;每片携带序号、长度、哈希和幂等键,可并行上传并查询缺失列表。服务端保存位图/状态,校验单片后落临时存储,全部到齐再按序合并并校验整体哈希,最后原子发布。重复、乱序和客户端重连不能产生重复数据;过期任务清理临时分片,合并与业务记录更新需要可恢复状态机。


155. 手动创建的对象如何纳入 Spring 容器?注入方式应该怎么选?

来源:华为通用软件开发二面,8 月 12 日

容器外 new 的对象不会自动经历依赖注入和完整 Bean 生命周期。可通过 @Bean 工厂方法、组件扫描或 registerBean 注册;确需对现有对象注入可使用 BeanFactory 的受控 API,但通常应重构创建边界。构造器注入能表达必需依赖、便于不可变和测试,应优先;setter 适合可选依赖,字段注入隐藏依赖且难测试。


156. Redis Stream、Pub/Sub 和 RabbitMQ 应该如何选择?

来源:华为通用软件开发二面,8 月 12 日

Pub/Sub 不持久化、无确认,消费者离线会丢消息,适合瞬时通知;Stream 有持久记录、消费组、pending 和确认,适合轻量可靠队列,但治理能力有限;RabbitMQ 提供路由、确认、持久化、死信、重试和成熟运维。选择应看可靠性、堆积、路由、顺序和团队基础设施,不要因为已有 Redis 就默认承担关键业务消息。


157. Java 虚拟线程、平台线程和异步编程应该怎么选?

来源:华为通用软件开发二面,8 月 12 日

虚拟线程由 JVM 调度,适合大量阻塞式 I/O,让同步代码保持可读;CPU 密集任务不会因线程更多而变快。平台线程适合数量受控、需要线程亲和或依赖 ThreadLocal/Native 行为的场景。回调/响应式异步能精确控制资源但增加传播和调试复杂度。上线前检查 pinning、连接池瓶颈、ThreadLocal 占用和限流,虚拟线程不能替代下游容量控制。


158. Git merge 和 rebase 有什么区别?已共享提交为什么不能随意 rebase?

来源:米哈游开发一面,8 月 12 日

merge 保留两条历史并创建合并提交;rebase 把提交复制到新基线,形成线性历史,但提交哈希会改变。只在本地未共享分支上自由 rebase;远端已被他人基于其开发时重写会造成重复提交和困难冲突。确需整理共享分支应先协调、使用 --force-with-lease 并留恢复点,主干通常用 merge/revert 保持可追溯。


159. 构建或 CI 概率失败时,如何判断应该阻断还是重试?

来源:米哈游开发一面,8 月 12 日

先按依赖下载、环境差异、资源争用、测试竞态和外部服务分类,并保存日志、制品、随机种子和环境指纹。重跑只用于确认 flaky,不能把“重试后通过”当成功;门禁应阻断并把不稳定用例隔离到负责人和修复 SLA 下。依赖锁定、Hermetic build、测试隔离和可重复环境消除根因;关键发布不得依赖概率通过。


160. Python list、tuple 和 dict 的语义、复杂度与大对象风险是什么?

来源:米哈游开发一面,8 月 12 日

list 是可变连续引用数组,尾部追加摊销 O(1),中间插删 O(n);tuple 不可变、可哈希取决于元素,适合作稳定记录;dict 是哈希表,平均查改 O(1),但有扩容和哈希退化风险。大 dict 会因稀疏桶、对象头和引用产生显著额外内存,并增加 GC 与缓存压力;应评估紧凑结构、分片、外部存储或批处理。


161. Python 多线程之间如何通信和安全终止?

来源:米哈游开发一面,8 月 12 日

优先使用 queue.Queue 传递任务和结果,它提供同步和背压;共享状态用 Lock/Condition/Event 保护,避免忙等。用 sentinel、Event 或取消协议通知退出,主线程 join 并处理未完成任务。GIL 不保证复合业务操作原子,也不适合 CPU 密集并行;CPU 任务用多进程或原生扩展。


162. 撤销/重做系统如何设计?

来源:米哈游开发一面,8 月 12 日

将用户动作建模为 Command,保存 execute/undo 所需的最小状态;已执行命令压入 undo 栈,撤销后移入 redo 栈,新动作会清空 redo 分支。大对象使用增量 diff、事件日志或快照 + 日志,不能每步复制全量状态。外部副作用需补偿命令且可能不可完全逆,必须明确失败与持久化恢复。


163. 冷热数据如何识别并分层存储?

来源:浦金科一面,8 月 12 日

依据访问频率、最近访问、业务价值、合规保留和恢复 SLA 分类,而非只按时间。热数据放高性能存储/缓存,温数据放低成本在线层,冷数据压缩归档到对象存储;迁移通过生命周期任务执行并保留索引。读取冷数据要支持异步恢复和回迁,删除/归档需审计。持续监控命中率、迁移成本和误判,避免热点突发时雪崩回源。


164. Raft 如何保证一致性?etcd 如何基于租约实现服务发现?

来源:成都晓多科技 Agent 开发岗二面,8 月 12 日

Raft 通过 Leader 选举、日志复制和多数派提交保证已提交日志不会被后续 Leader 覆盖;term 和日志新旧规则防止落后节点当选。服务实例把带租约的 key 写入 etcd 并周期续租,消费者 watch 前缀获得增删变化;租约过期会删除 key。客户端仍需处理 watch 断线、revision 压缩和全量重建,不能只依赖本地缓存。


165. Go sync.Map 适合什么场景?为什么不能替代普通 map + 锁?

来源:成都晓多科技 Agent 开发岗二面,8 月 12 日

sync.Map 适合写一次读多、或不同 goroutine 操作相互独立 key 的场景,通过只读快照和 dirty map 降低常见读取竞争。频繁覆盖同一 key、需要复合原子操作或类型安全时,普通 map + Mutex/RWMutex 更清晰可控。选型要基于读写比例和 benchmark,不因“并发安全”就默认使用。


166. Go 内存逃逸、泄漏和 pprof 应该如何联合排查?

来源:成都晓多科技 Agent 开发岗二面,8 月 12 日

逃逸是编译器决定对象需在堆上分配,不等于泄漏;用 -gcflags=-m 查看原因。泄漏表现为可达对象持续增长,常见于 goroutine、timer、缓存、切片底层数组和未关闭资源。用 pprof 的 heap、allocs、goroutine 与 diff 对比定位增长路径,结合 GC 和业务指标复现;修复生命周期和引用后再考虑池化,避免用 sync.Pool 掩盖泄漏。


167. 虚拟机、容器和 Kubernetes 分别解决什么问题?

来源:小舒一面

虚拟机虚拟整套硬件和内核,隔离强但启动和资源成本高;容器共享宿主内核,通过 namespace/cgroup 隔离,启动快但安全边界更薄;Kubernetes 不是另一种容器,而是编排平台,负责调度、服务发现、声明式收敛和故障恢复。强隔离/异构内核用 VM,大量一致应用用容器,规模化运维再引入 K8s;也可用 microVM 组合边界与速度。


168. writev 如何通过聚合缓冲区减少系统调用?

来源:拼多多 AI Agent 岗技术面

writev 接收多个 iovec,在一次系统调用中把分散缓冲区按序写出,避免先拷贝到一个连续用户缓冲区,并减少用户态/内核态切换。它不保证一次写完,非阻塞 socket 仍需处理部分写入并推进各 iovec;数量受系统上限约束。适合协议头+正文、日志批量和网络聚合,但要控制批量等待延迟与单次大小。


169. JWT 如何支持权限变更后的即时失效?

来源:小厂 Agent 全栈实习面经

短寿命 access token 降低过期窗口,refresh token 由服务端会话记录管理并轮换。需要即时失效时校验 jti denylist、用户 session/version 或 permissions_updated_at,使旧版本 Token 拒绝;高风险接口可每次查集中授权服务。这样会重新引入状态和可用性成本,应按风险分级,而不是声称 JWT 天然无法撤销或完全无状态。


170. cgroup 如何限制容器的 CPU、内存和进程资源?

来源:B站 Agent 一面

cgroup v2 通过统一层级控制 CPU weight/max、memory max/high、pids max 和 I/O 等资源。达到 CPU 配额会 throttling,超过内存上限可能触发 cgroup 内 OOM,进程数限制可防 fork bomb。它负责计量与限制,不负责文件/网络/进程视图隔离,后者由 namespace 和安全策略完成。排查应读取 cgroup 指标而非只看宿主机 top。


171. Java 类卸载需要满足哪些条件?为什么类加载器泄漏会阻止卸载?

来源:淘天 AI 应用开发一面

类通常只有在其定义 ClassLoader 不再可达、该加载器加载的 Class 无实例且对应 Class 对象不可达时才可卸载,并依赖 GC 策略。线程上下文加载器、静态集合、ThreadLocal、JDBC Driver、日志框架或回调注册若持有应用 ClassLoader,会让整批类和元空间无法回收。用 class histogram、JFR/heap dump 查看加载器保留链,修复注册与生命周期,而不是只扩大 Metaspace。


172. SSRF 的攻击面和防御方案是什么?

来源:字节抖音 Agent 一面

SSRF 诱导服务端请求攻击者指定地址,可访问云元数据、内网管理面或绕过网络边界。防御要解析并规范化 URL,只允许受信协议/域名/端口;DNS 解析后校验所有 IP,阻止私网、环回、链路本地和重绑定,重定向后再次校验。请求通过受控代理与网络 egress 策略执行,限制响应大小/时间并隔离凭据。黑名单字符串匹配不足以抵御编码和 DNS 变化。


173. Aerospike 与 Redis 的存储机制和适用场景有什么区别?

来源:字节抖音 Agent 一面

Redis 以丰富数据结构和内存操作见长,可配置持久化与集群,适合缓存、计数、队列和实时结构操作。Aerospike 面向大规模低延迟 KV,索引常驻内存、数据可放 SSD,强调分区、高可用和可预测延迟,查询与数据结构能力相对受限。选型看数据规模、访问模型、持久性、尾延迟、运维和成本,不能简单理解为“磁盘版 Redis”。


174. HTTP Keep-Alive 和客户端连接池是什么关系?

来源:深信服 Agent 开发一面

Keep-Alive 表示一个 TCP/QUIC 连接可承载多个 HTTP 请求;连接池是客户端管理多个可复用连接的资源策略,负责上限、空闲超时、健康检查、排队和按目标分组。没有池会频繁握手,有 Keep-Alive 但池无限也会耗尽端口/下游连接。HTTP/2/3 还可在单连接多路复用,但仍需处理连接级故障、并发流限制和负载均衡。


175. HTTP/1.1、HTTP/2 和 HTTP/3 的核心差异是什么?

来源:深信服 Agent 开发一面

HTTP/1.1 文本协议,持久连接但并发常需多连接;HTTP/2 二进制帧、头部压缩和流多路复用,但 TCP 丢包会阻塞同连接所有流;HTTP/3 基于 QUIC/UDP,每个流独立可靠传输,降低传输层队头阻塞并支持更快握手和连接迁移。HTTP/3 不是不可靠 UDP 直传,可靠性由 QUIC 实现;选型还受代理、网络和 CPU 成本影响。


176. 旧数据如何在线迁移并保证双写一致与可回滚?

来源:深信服 Agent 开发一面

先建立新旧 schema 映射和幂等迁移键,离线回填历史并校验数量、哈希和业务不变量;增量阶段通过 CDC 或事务 outbox 同步。切流前先双读比对,再小流量读新写双;双写要有明确主事实源、失败补偿和对账,不能假设两个写原子完成。切换使用版本开关,保留旧数据和反向同步窗口,确认稳定后再停止旧链路。


177. Docker/Kubernetes 中 bridge、host、overlay 和 CNI 网络模式如何选择?

来源:字节社招一面,2026 年 8 月 23 日

Docker bridge 在单机上通过 Linux bridge、veth 和 NAT 连接容器,隔离性与通用性较好,但跨主机需要额外网络;host 模式让容器直接共享宿主机网络命名空间,少一层转发且便于使用宿主端口,却会削弱隔离、产生端口冲突,也不等于绕过所有协议栈开销。overlay 用隧道把多台宿主机上的容器组成逻辑二层或三层网络,部署灵活,但封装会带来 MTU、排障和性能成本。

Kubernetes 的 CNI 不是一种固定网络模式,而是节点侧配置 Pod 网络的接口规范及插件生态。应根据是否跨节点、网络策略、吞吐与尾延迟、云厂商路由能力、IP 数量和运维复杂度选择插件及数据平面:普通隔离容器可用 bridge,极致性能或必须绑定宿主网络的系统组件才考虑 host,跨主机容器网络可用 overlay;具备可路由 Pod CIDR 或云原生网卡时可选无隧道路由方案。最终还要验证 MTU、NetworkPolicy、Service 转发、可观测性和故障恢复,不能只按理论带宽选型。


178. TLS 根证书如何建立对服务端身份的信任?

来源:字节抖音电商一面,2026 年 8 月 24 日

客户端本地信任库预置的是受信根 CA 证书或信任锚,而不是所有网站证书。服务端在握手中发送叶子证书和通常所需的中间证书;客户端逐级验证签名,直到链条连接到本地信任锚,同时检查证书有效期、用途约束、Basic Constraints、Name Constraints 等规则。根证书通常是自签名的,它之所以可信来自操作系统、浏览器或组织的分发与治理,而不是“自签名本身证明可信”。

完成证书链验证后,客户端还必须校验访问主机名是否匹配叶子证书 SAN,并在策略要求下检查吊销状态;握手中的 CertificateVerify 则证明服务端持有叶子证书对应的私钥。任一环节失败都不能建立服务端身份信任。企业私有 CA 需把根证书安全地下发到受控信任库,并处理轮换和撤销,不能通过关闭证书校验解决自签名证书问题。


179. MyBatis 的 #{}${} 有什么区别?如何防止 SQL 注入?

来源:小公司 AI Agent 开发一面,2026 年 8 月 24 日

#{} 会生成 JDBC PreparedStatement 的参数占位符,值由驱动绑定并按类型转义,数据不会被解释为 SQL 结构;${} 是在 SQL 解析前进行原始字符串替换,可用于无法参数化的表名、列名或排序方向,但用户输入一旦直接进入其中就可能改变 SQL 语义。#{} 还能结合 TypeHandler 处理类型,但不能把列名当作绑定值,因此不能机械地用它替换所有 ${}

防注入的默认策略是所有数据值使用 #{},动态结构通过代码枚举或白名单映射为固定 SQL 片段,例如把客户端的排序字段映射到允许的列名,并限制 ASC/DESC。同时使用最小权限数据库账号、限制批量与多语句执行、记录异常查询并测试边界输入。手工转义、正则黑名单和“前端已校验”都不能替代参数绑定与结构白名单。


180. 实时热点榜单在高并发下如何设计数据结构、缓存、数据库与一致性?

来源:字节 Agent 开发一面,2026 年 8 月 24 日

先定义榜单窗口、分数函数、去重口径、更新延迟和精确度要求。写入链路把浏览、点赞等事件追加到消息队列,由流处理按内容 ID 聚合并计算时间衰减分数;热点候选可按时间分桶维护,在线 Top-N 使用 Redis Sorted Set 或分片 Top-K,避免每次请求扫描全量数据。读侧通过本地缓存/CDN 和 Redis 提供版本化榜单,数据库或数据湖保存事件与聚合结果,承担审计、重算和容灾,而不是让关系库直接承受每次排行更新。

高并发下可按业务、地域或哈希分片计算局部 Top-K,再由归并层生成全局榜单;热 key 通过分片计数、批量合并和读副本缓解。事件使用唯一 ID 幂等消费,乱序事件按事件时间和水位线处理,分数更新用 Lua、事务或版本比较保证单 key 原子性。缓存与持久层通常选择最终一致:先可靠记录事件,再异步更新榜单,定期从事实数据校准;发布榜单时用新版本构建完成后原子切换,避免读到半成品,并明确允许的陈旧窗口、降级快照和重放恢复方案。


181. 只有 1 GB 物理内存且没有 swap,进程能否打开或访问 2 GB 文件?

来源:字节抖音电商一面,2026 年 8 月 24 日

可以“打开”文件:open 主要创建文件描述符,并不把文件内容全部载入内存。也可以用 read 分块顺序处理,内核页缓存中的旧页可回收,因此文件大小不受物理内存容量直接限制。若使用 mmap,映射先占用一段虚拟地址空间,页面在首次访问时按需缺页并从文件加载;干净的文件页在内存压力下可以丢弃,之后再次从文件读取,所以也不要求 2 GB 页面同时驻留物理内存。

边界取决于进程虚拟地址空间、映射长度、内存限制和访问方式:32 位进程可能找不到足够连续虚拟地址,容器/cgroup 或 RLIMIT_AS 可能拒绝映射;大量随机访问会频繁缺页并产生抖动。没有 swap 不妨碍回收干净文件页,但匿名内存和脏的私有页缺少换出空间,更容易触发分配失败或 OOM。若把 2 GB 文件一次性读入匿名缓冲区,或使用会产生大量 copy-on-write 脏页的 MAP_PRIVATE 写入,就很可能失败;稳妥方案是分块读取或窗口化映射,并处理 mmap/缺页期间的 I/O 错误以及文件被截断导致的 SIGBUS


182. TLS 1.3 在 HTTP/1.1、HTTP/2 与 HTTP/3 中如何完成认证、密钥协商和加密?

来源:字节 AI 开发实习一面,2026 年 2 月 27 日

先区分承载关系:HTTP/1.1 和 HTTP/2 的 HTTPS 通常先建立 TCP 连接,再在其上完成 TLS 1.3 握手;HTTP/3 则运行在基于 UDP 的 QUIC 中,TLS 1.3 握手被集成进 QUIC,不存在独立的 TCP 连接或 TLS record layer。三者都使用 TLS 1.3 的认证与密钥派生能力,但报文承载和数据保护层不同。

在 TCP + TLS 1.3 链路中,客户端通过 ClientHello 发送支持版本、密码套件、SNI、ALPN、supported groups 和临时 ECDHE key share;服务端在 ServerHello 中完成选择并返回自己的 key share。双方由 ECDHE 得到共享秘密,再通过 HKDF 结合握手 transcript 派生握手流量密钥。ECDHE 提供前向保密;TLS 1.3 密码套件主要选择 AEAD 和 HKDF 使用的哈希算法,签名算法与密钥交换参数分别协商,不是用证书公钥加密全部业务数据。

随后服务端发送证书链,并用证书私钥对当前握手 transcript 签名。客户端需要验证证书链能否连接到本地信任锚,同时检查有效期、SAN 主机名、Key Usage/Extended Key Usage、CA 约束和按策略要求的吊销状态;再验证 CertificateVerify,确认对端确实持有叶子证书对应的私钥。双方的 Finished 消息对完整握手摘要做密钥校验,防止协商参数或证书消息被篡改;需要双向认证时,客户端还会发送自己的证书和签名。

握手完成后,双方再派生应用流量密钥。HTTP/1.1 和 HTTP/2 数据进入 TLS record,由 AES-GCM 或 ChaCha20-Poly1305 等 AEAD 加密并校验完整性;ALPN 分别协商 http/1.1h2。HTTP/3 中,TLS 握手消息由 QUIC CRYPTO frame 承载并通过 ALPN 协商 h3,TLS 导出的秘密进一步生成 QUIC packet protection keys,由 QUIC 保护承载 HTTP/3 frame 的数据包。

证书解决“对方身份与公钥是否可信”,CertificateVerify 证明“对端持有对应私钥且握手未被冒充”,ECDHE 解决“如何得到共享秘密”,对称 AEAD 才承担批量数据保护。排障时必须先确认是 TCP + TLS 的 HTTP/1.1/2,还是 QUIC 内集成 TLS 1.3 的 HTTP/3,不能把“先 TCP、再 TLS”套到 HTTP/3。


183. AQS 的 state 和等待队列如何工作?ReentrantReadWriteLock 怎样实现共享读与独占写?

来源:字节 Agent 研发面经,2026 年 3 月 4 日

AQS 把同步器拆成两部分:一个 volatile int state 表示同步状态,子类通过 CAS 定义获取和释放条件;竞争失败的线程进入近似 FIFO 的 CLH 变体等待队列,在前驱状态允许时被 unpark 后重新尝试。独占模式同一时刻只允许一个所有者成功,共享模式则允许多个节点同时成功并向后传播唤醒。ConditionObject 另有条件等待队列,await 会先完整释放锁,收到 signal 后转移到同步队列重新竞争,因此 signal 不等于立即获得锁。

ReentrantReadWriteLock 的同步器把 32 位 state 分段使用:低 16 位记录写锁重入次数,高 16 位记录总读锁次数。写锁按独占模式获取,并额外记录 owner,只有写线程能重入和释放;读锁按共享模式获取,多个读线程可同时增加高位计数,同时还要维护每个线程自己的读重入次数,防止错误释放。存在其他线程持有写锁时读锁失败;读锁未清空时其他线程也不能获得写锁。

公平模式按队列先后抑制插队,非公平模式允许一定抢占以换吞吐,但写线程仍可能遭遇持续读流量带来的延迟。持有写锁的线程可以先获得读锁再释放写锁,实现锁降级;普通读线程不能安全地直接升级为写锁,否则多个升级者可能互相等待。state 只是同步协议的载体,线程安全还依赖 CAS、队列唤醒、owner/读计数和内存语义共同实现,不能把 AQS 简化成“一个整数加链表”。


184. 微服务中的分布式事务有哪些方案?应该如何选型?

来源:北京四维图新面经,2026 年 3 月 6 日

先判断业务是否真的需要跨服务强一致。能通过调整服务边界把更新放进一个本地事务,或把强约束收敛到单一事实源,通常比引入分布式事务更可靠。必须跨资源时,XA/2PC 由协调者组织 prepare 和 commit,可提供较强原子性,但会占用资源、依赖参与者协议并承受协调者故障和长事务阻塞;适合参与方可控、事务短且强一致收益高的场景,不适合任意外部服务。

TCC 把业务显式拆成 Try、Confirm、Cancel:Try 预留资源,后两步必须幂等并处理空回滚、悬挂和重复调用,控制力强但业务侵入和实现成本高,常用于资金、库存等可冻结资源。Saga 把长流程拆成本地事务与反向补偿,吞吐和可用性较好,但只能达到最终一致,补偿也未必等价于物理回滚。

Transactional Outbox 是把业务更新和一条待发布的 outbox 事件写入同一个本地数据库事务,提交后再由轮询发布器或 CDC 读取 outbox 并投递消息。CDC 本身只是从已提交的 binlog/WAL 等变更日志捕获记录,不参与前面的本地事务;若直接从业务表推导事件,会增加表结构与事件语义的耦合。两种投递路径通常都是 at-least-once,发布端要保存读取位点并处理顺序,消费者仍需按事件 ID 幂等。它们解决的是本地提交后可靠传播事件,不自动保证跨服务全局不变量。

选型要同时看一致性等级、隔离要求、事务时长、参与方数量、是否可补偿、峰值吞吐和故障恢复成本。无论采用哪种方案,都要有全局业务 ID、幂等键、状态机、超时与有界重试、去重、对账、人工修复和可观测性;补偿失败需要进入持续重试或人工处置,而不是吞掉异常。实际系统常混合使用,例如核心记账在本地强事务中完成,跨服务通知通过 outbox 最终一致,只有少数可冻结资源的步骤使用 TCC。


185. OSI 七层分别负责什么?数据如何逐层封装和解封装?

来源:网新软件面经,2026 年 4 月 14 日

OSI 从上到下是:应用层提供面向应用的网络服务,如 HTTP、DNS、SMTP;表示层负责数据表示、编码、压缩和加密;会话层管理会话建立、同步和恢复;传输层提供端到端传输、端口、可靠性和流量控制,如 TCP/UDP;网络层负责逻辑寻址和跨网络路由,如 IP;数据链路层负责同一链路上的成帧、MAC 寻址和差错检测,如 Ethernet;物理层把比特转换为电、光或无线信号。现实 TCP/IP 协议栈不会严格按七层实现,例如 TLS 常被视为位于应用与传输之间,因此七层更适合职责分析而不是进程边界图。

发送端把应用数据交给下一层:传输层加入 TCP/UDP 首部形成 segment/datagram,网络层加入 IP 首部形成 packet,链路层再加入帧头和通常的校验尾部形成 frame,最后由物理层发送比特。接收端反向检查并去除相应头部,把 payload 逐层交给上层。每层只把上层整体视为自己的载荷,这就是封装与解封装。

链路层封装只在一跳内有效:交换机主要按 MAC 转发,路由器收到帧后会去掉旧链路层头部、处理 IP 包,再为下一跳重新封装,因此 MAC 地址通常逐跳变化,IP 地址在无 NAT 时保持端到端不变。端口把报文交给目标进程,应用协议再解释业务语义。排障时按层定位更有效,例如链路是否通、IP 路由是否正确、TCP 是否建立、TLS 是否认证、HTTP 是否返回,而不是把所有“访问失败”都归因于网络不通。


186. Python GIL 为什么限制 CPU 密集型多线程?I/O 与 CPU 任务应如何选择并发模型?

来源:腾讯后台一面,2026 年 3 月 27 日

在传统 CPython 默认构建中,GIL 保证同一解释器进程内通常只有一个线程执行 Python 字节码,简化了引用计数和解释器内部状态保护。CPU 密集的纯 Python 线程即使分布在多个核心上,也要竞争 GIL,并产生切换开销,因此通常无法获得按核心数增长的并行加速。GIL 不等于业务数据天然线程安全:一次看似简单的复合操作仍可能跨多个字节码,I/O 和扩展代码也可能释放 GIL,共享状态仍需正确同步。

I/O 密集且调用是阻塞接口时,线程池简单实用,因为线程等待网络或磁盘期间会释放 GIL;连接数很大、协议库支持异步时,asyncio 用单线程事件循环和协程减少线程开销,但所有阻塞调用都必须隔离,否则会卡住整个循环。CPU 密集的纯 Python 代码优先用多进程或 ProcessPoolExecutor 利用多个核心,同时权衡序列化、进程内存和进程间通信成本;NumPy 等原生扩展若在重计算时释放 GIL,也可在线程中并行。

自由线程构建、子解释器或释放 GIL 的原生代码提供了额外选择,但要先验证依赖兼容性、共享状态安全和实际 benchmark,不能只看理论并行。混合服务通常分层处理:异步或线程承接 I/O,受限队列提供背压,CPU 阶段送入进程池或独立计算服务,并分别设置并发上限、超时和取消传播。模型选择取决于任务特征,而不是笼统地说“Python 不能多线程”。


187. Maven 的传递依赖冲突如何发现和治理?

来源:滴滴 AI Agent 后端面经,2026 年 7 月

Maven 会沿依赖图引入非 optional 的传递依赖,并按 scope 规则决定是否进入编译、测试或运行时 classpath。同一构件出现多个版本时,默认采用“路径最近者优先”;深度相同则通常由声明顺序决定。构建成功不代表版本兼容,冲突可能直到运行时才表现为 NoSuchMethodErrorClassNotFoundException 或行为变化,因此先用 mvn dependency:tree -Dverbose、IDE 依赖分析或有效 POM 找出版本来源、scope 和被省略节点。

治理时在父 POM 的 dependencyManagement 中集中锁定版本,或通过 type=pomscope=import 导入经过验证的 BOM;它只管理版本和默认属性,不会自动把依赖加入项目。对不需要或明显冲突的传递依赖,在直接依赖上使用 exclusions,再显式声明真正需要的版本。排除应尽量靠近引入源且说明原因,不能全局大面积排除后靠偶然的 classpath 通过。

持续治理可在 CI 使用 Maven Enforcer 的 dependency convergence、upper-bound 等规则,并配合锁定插件版本、依赖漏洞扫描和集成测试。升级前检查二进制兼容、框架 BOM 支持范围以及同一依赖的成组组件,发布产物还应核对实际打包内容。最终目标不是让依赖树只有一个版本号,而是让选中的版本明确、可重复构建且经过运行路径验证。


188. Python 的 LockRLockSemaphore 有什么区别?分别适合什么场景?

来源:字节 Agent 开发一面,2026 年 7 月

threading.Lock 是不记录递归层级的互斥锁,同一时刻只允许一个线程进入临界区;持锁线程再次 acquire 会把自己阻塞,因此适合无嵌套获取的短小共享状态保护。RLock 记录拥有者和重入计数,同一线程可以多次获取,但必须对应释放相同次数后其他线程才能进入;适合公共方法与内部方法都会获取同一把锁的递归或分层调用,代价是状态更复杂,不应拿它掩盖不清晰的锁边界。

Semaphore(n) 维护许可计数,最多允许 n 个线程同时通过,适合限制数据库连接、外部接口或工作槽位等有限资源的并发量,而不是保护必须严格单写的对象;BoundedSemaphore 还能在释放次数超过初始值时尽早暴露错误。三者都支持 with,应利用上下文管理保证异常路径释放,并避免持锁执行慢 I/O、无序获取多把锁或无限等待。

GIL 不能替代这些同步原语,因为线程可能在 I/O、扩展代码或字节码切换点交错执行。需要等待某个状态条件时使用 Condition,只做一次性通知可用 Event,生产者消费者优先使用 queue.Queue,不要用锁加轮询自行拼装。选择标准是要保护“不变量”、允许“同一拥有者重入”,还是限制“同时使用资源的数量”。


189. Java 的方法重载与方法重写有什么区别?

来源:线下某小厂面经,2026 年 5 月 14 日

重载发生在同一个类或可见的继承方法集合中:方法名相同,但参数列表的数量、类型或顺序不同。编译器根据调用点的静态类型选择最匹配签名,因此它是编译期多态;返回类型、参数名或 throws 列表不能单独构成重载。自动装箱、基本类型提升、可变参数和 null 可能让候选选择不直观甚至产生歧义,公共 API 应避免设计难以判断的重载组合。

重写发生在子类对父类可继承实例方法提供相同签名的实现,返回类型可协变。真正调用哪个实现由运行时对象类型决定,因此它是运行期多态。重写方法不能降低访问权限,不能声明比父方法更宽的受检异常;final 方法不能重写,构造器不能继承或重写,private 方法对子类不可见,static 方法只会按静态类型隐藏而不是动态重写。

@Override 能让编译器检查签名,避免因为参数细微变化而意外写成重载。面试中可用 Parent p = new Child() 说明:p.instanceMethod() 对重写进行动态分派,但选择哪个重载仍先由变量 p 的编译期类型和实参静态类型决定。区分两者的关键不是“名字一样”,而是签名关系、绑定时机和是否参与动态分派。


190. C++ 模板函数在什么时候编译和实例化?为什么实现通常放在头文件?

来源:三星 AI Infra 实习一面,2026 年 5 月 1 日

编译器先解析模板定义并检查不依赖模板参数的语法;遇到具体使用时,再用实参完成实例化并检查依赖类型的表达式。隐式实例化要求使用点能够看到完整定义,因此模板实现通常放在头文件。只在 .cpp 中定义而没有显式实例化,其他翻译单元往往只能看到声明,最终会出现链接错误。

显式实例化可以在一个翻译单元生成指定类型版本,配合 extern template 抑制其他翻译单元重复实例化,降低编译时间和代码膨胀;代价是支持的类型集合需要预先确定。模板的两阶段查找、依赖名、SFINAE/Concepts 会影响重载选择,但“模板在运行时生成代码”是错误说法。


191. 遍历 STL 容器时如何安全删除元素?

来源:三星 AI Infra 实习一面,2026 年 5 月 1 日

不能在 for (++it) 循环里调用 erase(it) 后继续使用旧迭代器。常见写法是 it = container.erase(it),由 erase 返回下一个有效迭代器;不删除时才执行 ++it。C++20 以后,按谓词批量删除还可以优先使用 std::erase_if

失效范围取决于容器:list 通常只使被删元素的迭代器失效;vector/deque 删除位置及其后的迭代器可能失效;关联容器通常只使被删节点失效。并发修改还需要外部同步,迭代器规则不提供线程安全。


192. 如何让函数在 main 之前执行?有哪些工程风险?

来源:百度 AI Infra 实习一面,2026 年 5 月 2 日

C++ 可通过具有静态存储期的对象构造函数完成初始化,也可以使用编译器扩展的 constructor attribute;C 常见实现依赖启动段或编译器属性。它们最终都由运行时启动代码在进入 main 前调用,不是操作系统直接调用普通业务函数。

跨翻译单元的静态初始化顺序通常不应依赖,这就是 static initialization order fiasco。更稳妥的做法是函数内静态对象、显式初始化入口或可控的注册表;初始化逻辑应避免依赖尚未构造的全局对象、启动线程或执行难以恢复的 I/O。


193. 条件断点、调用栈和内存观察分别适合排查什么问题?

来源:百度 AI Infra 实习一面,2026 年 5 月 2 日

条件断点只在表达式满足时停下,适合循环中特定索引、对象 ID 或错误状态;命中次数断点适合定位第 N 次异常。调用栈展示当前线程从入口到故障点的调用链和栈帧,可逐层查看参数、局部变量和返回地址。Watch/内存窗口用于持续观察表达式、地址和对象布局,硬件 watchpoint 还可以在某段内存被读写时暂停。

优化构建可能内联函数、删除变量或重排指令,导致源码行与现场不一一对应。生产问题要保留符号、构建 ID、核心转储和对应二进制,并结合日志、sanitizer、perf 等证据;不要为了方便调试就长期关闭所有优化后推断线上性能行为。


194. std::deque 的底层结构和迭代器失效规则是什么?

来源:百度 AI Infra 实习一面,2026 年 5 月 2 日

deque 通常由一张 map 指向多个固定大小的连续缓冲块,因此支持两端近似常数时间插入删除,也支持随机访问;它不像 vector 那样保证全部元素位于一段连续内存,常数开销和缓存局部性通常更差。具体块大小和 map 增长方式属于标准库实现细节。

中间插入删除可能移动元素并广泛使迭代器失效;两端操作对迭代器、指针和引用的影响要按所用标准版本与实现契约确认。需要连续存储和与 C API 互操作时优先 vector,需要稳定节点地址时考虑 list,不能只凭“大 O 相同”选容器。


195. shared_ptr 是线程安全的吗?

来源:抖音搜推 AI Infra 一面,2026 年 5 月 1 日

不同 shared_ptr 对象即使共享同一控制块,也可以在不同线程并发复制和销毁,因为引用计数更新具备所需同步;这不代表所指对象线程安全,也不代表多个线程可以无锁修改同一个 shared_ptr 变量。后两种情况仍可能发生数据竞争。

共享同一个指针变量时使用互斥锁,或使用标准提供的原子 shared_ptr 操作。引用计数只管理生命周期,不保护对象内部不变量;同时要用 weak_ptr 打破循环引用,并警惕频繁跨核更新控制块造成的缓存争用。


196. 什么是 Cache 竞争和 False Sharing?如何定位?

来源:摩尔线程 AI Infra 一面,2026 年 5 月 2 日

多个核心访问同一共享数据会触发一致性流量;即使线程修改的是不同变量,只要变量落在同一 Cache Line,也会让缓存行在核心间反复失效,这就是 False Sharing。它与容量不够导致的 capacity miss、映射冲突导致的 conflict miss、内存带宽饱和不是同一个问题。

先用硬件性能计数器、Profiler 和 CPU 亲和性实验确认 cache miss、HITM/一致性事件和带宽,再检查热点结构布局。常见优化包括按线程分片、局部累加后合并、填充或对齐热点写字段、减少共享写入;盲目 padding 会增大工作集,因此必须用基准验证。


197. FP16、FP32、FP64 的位宽如何分配?数值范围和精度如何权衡?

来源:飞腾 AI Infra 实习一面,2026 年 5 月 2 日

IEEE 754 binary16 通常为 1 位符号、5 位指数、10 位显式 fraction;binary32 为 1/8/23;binary64 为 1/11/52。正规数还包含隐含的最高有效位,因此有效精度比 fraction 位数多一位。全零和全一指数分别用于次正规数/零以及无穷/NaN 等特殊值。

指数位决定动态范围,fraction 位决定有效精度。低精度能降低存储、带宽和计算成本,却更容易溢出、下溢和累积误差。AI 训练常使用混合精度、FP32 累加和 Loss Scaling;具体硬件对 FP16、BF16、FP8 的吞吐与舍入支持不同,不能只比较位宽。


198. git fetch + checkoutgit pull 和远程跟踪分支有什么区别?

来源:飞腾 AI Infra 实习一面,2026 年 5 月 2 日

git fetch 只把远端引用和对象更新到本地,不修改当前工作分支;之后可以检查 origin/x,再创建或切换本地分支。git pull 相当于先 fetch,再按配置 merge 或 rebase 到当前分支,因此会直接改变当前分支历史和工作区状态。

远程跟踪分支如 origin/main 是本地记录的远端状态快照,不是能直接在本地提交的远端分支。团队使用前应明确 pull 的 merge/rebase 策略,操作前检查脏工作区和上游绑定;自动化环境更适合拆开 fetch、验证目标 commit,再执行明确的 merge/rebase。


199. Go 和 Java 的主要差异应从哪些维度比较?

来源:阿里云 Agent Infra 一面,2026 年 4 月 1 日

不要只回答“Go 快、Java 重”。语言层面,Go 强调较小语法、组合和 goroutine/channel,Java 提供类继承、泛型、注解和成熟的 JVM 生态;运行时层面,Go 通常静态编译并由自身 runtime 调度 goroutine,Java 编译到字节码后由 JVM 解释/JIT,线程与虚拟线程的模型也不同。

两者都有 GC、并发库、逃逸和性能调优问题。选型要看延迟/吞吐目标、启动和内存约束、团队库生态、诊断工具、部署方式以及业务框架。Go 常适合云原生服务和工具,Java 在复杂企业业务与中间件生态中优势明显,但具体结论必须用目标负载验证。


200. gRPC 为什么常使用 Protobuf 二进制编码?它一定比 JSON 快吗?

来源:阿里云 Agent Infra 一面,2026 年 4 月 1 日;拼多多 AI 全栈两轮技术面

gRPC 通常以 Protobuf 定义强类型契约,使用字段编号编码,消息一般比包含字段名的 JSON 紧凑,并提供代码生成、双向流和基于 HTTP/2 的多路复用。它适合内部服务间高频 RPC,但可读性、调试门槛、浏览器支持和协议演进纪律也要考虑。

二进制并不保证任何场景都更快:小消息可能被网络、TLS、排队和业务处理主导,压缩也有 CPU 成本。字段演进应保留编号、谨慎修改语义,并传播 Deadline、取消和 Trace。对外开放、浏览器直连或强调可调试性时,JSON/REST 仍可能更合适。


201. 按时间分表后,跨表查询如何实现全局排序和分页?

来源:虾皮 AI Infra 二面,2026 年 4 月 13 日

先根据时间条件路由到有限分表,在每个分片使用相同排序键执行有界查询,再在聚合层做 k-way merge。排序键必须全局稳定,例如 (created_at, id),否则相同时间戳会导致重复或漏项。深分页不宜对所有分片执行巨大 offset,优先使用基于上一页排序键的 keyset/cursor pagination。

Cursor 应携带各分片推进位置或一个能重新计算路由的全局边界,并绑定查询条件和版本。还要定义跨月写入、迟到数据、迁移和归档期间的一致性语义。若查询长期跨大量分片,应考虑二级索引、汇总表或搜索系统,而不是让在线请求无界 fan-out。


202. AI 流式请求如何传播超时、取消和背压?

来源:牛客 AI Infra 面试汇总,2026 年 6 月 3 日;拼多多 AI 全栈两轮技术面

Gateway 收到客户端断连或取消后,应沿调用链取消 HTTP/gRPC 请求、模型排队项和正在生成的序列,并释放 KV Cache、CPU buffer 和计费状态。每一层都使用绝对 Deadline 或可比较的剩余预算,避免层层独立超时导致总时长失控;操作已经产生副作用时,取消只代表“不再等待”,不等于副作用回滚。

背压要从最慢消费者向上游传播:限制单连接缓冲,合并过小 Token Chunk,设置写超时和慢消费者策略;跨服务流式透传时保留 request ID、sequence、finish reason 和 usage。断线续传只能从已持久化且有序的边界恢复,不能默认重放模型采样得到相同 Token;重试前还要区分“尚未接收首 Token”和“已向用户展示部分结果”。


203. 如何用 Shell 安全、确定性地合并目录中的多个文件?

来源:蔚来自动驾驶 Infra 实习面经

先明确合并顺序、文件类型和输出格式。不要直接依赖 shell glob 的偶然顺序,也不要用 for f in $(find ...),因为空格、换行和通配符会破坏文件名边界。Linux 下可以用 NUL 分隔传递路径,例如先把输出写到目录外的临时文件,再执行 find "$dir" -maxdepth 1 -type f -print0 | LC_ALL=C sort -z | xargs -0 cat -- > "$tmp",成功后原子移动到目标位置。

还要处理空目录、权限失败、合并过程中源文件变化和输出文件被再次读入。文本文件若要求文件间换行或标头,应显式插入分隔符;二进制文件只能按字节拼接,能否得到有效文件取决于格式本身。生产脚本应启用严格错误处理、检查退出码,并在失败时清理临时文件,不能留下看似成功的半成品。


204. 操作系统如何管理内存?为什么要把虚拟内存与物理内存分开?

来源:蔚来自动驾驶 Infra 实习面经荣耀 AI Infra 一面字节 AI Infra 实习面经AI Infra 应届春招面经、字节 AML / 火山方舟 AI Infra 一面

操作系统同时管理物理页分配、每个进程的虚拟地址空间与页表、匿名页和文件映射、页缓存、共享内存、回收/Swap、NUMA 放置以及 cgroup 限额。分配路径先满足虚拟地址和权限,再在访问或预取时建立物理页映射;内核根据工作集和内存压力回收页面,而不是让应用直接持有固定物理地址。

虚拟地址与物理地址分开带来四个核心能力:进程隔离和权限保护;程序可在统一地址布局运行而无需知道物理位置;按需分页、页面置换和 overcommit 提高物理内存利用率;多个进程可共享只读代码、页缓存或显式共享内存。页表把虚拟页映射到物理页并记录读写、执行和用户/内核权限,TLB 缓存近期转换;Fork 后常以写时复制共享页面,只有写入时才复制。

访问没有有效映射或页面尚未驻留时会触发缺页异常:匿名零页、文件页可按需建立映射,已换出的匿名页或未缓存的文件页可能需要磁盘 I/O。内存压力下内核按近似 LRU 等策略回收干净文件页、回写脏页或把匿名页换到 Swap;若可回收页不足、Swap 不可用或受 cgroup/进程限制,分配可能失败并触发 OOM 处理。排障要结合缺页率、工作集、换入换出、页缓存、RSS、cgroup 限额和 OOM 日志,而不是只看 free memory。


205. TCP 与 UDP 的差异、适用场景和可靠性边界是什么?

来源:蔚来自动驾驶 Infra 实习面经腾讯 CDG AI Infra 框架侧面经

TCP 是面向连接的可靠字节流,通过序列号、确认、重传、流量控制和拥塞控制提供按序、无重复的传输;应用必须自行做消息分帧。UDP 保留报文边界,无连接状态,协议本身不保证送达、顺序或去重,也没有 TCP 式拥塞控制,但头部和连接管理开销更小,允许应用按业务自行选择可靠性策略。

文件传输、数据库连接和多数 HTTP/1.1、HTTP/2 流量通常选择 TCP;DNS 查询、实时音视频和游戏状态同步常使用 UDP,以容忍少量丢包换取更及时的数据。UDP 不等于“不可靠应用”:QUIC 在 UDP 之上实现可靠流、拥塞控制和加密。选型应看消息边界、丢包语义、时延、网络公平性、NAT/防火墙和实现复杂度,不能只回答“TCP 慢、UDP 快”。


206. 哈希表如何处理冲突和扩容?std::mapstd::unordered_map 如何选择?

来源:小鹏汽车 AI Infra 面经阿里云 AI Infra 一面沐曦 AI Infra 一面

哈希表用哈希函数把 key 映射到桶,冲突可用链地址法、开放寻址等方式处理;负载因子过高时通常扩桶并重新分布元素。理想散列下查找和插入平均为 O(1),但冲突严重时可能退化,扩容还会产生一次 O(n) 的搬迁成本。std::unordered_map 保证平均常数复杂度,但标准不承诺具体桶结构,也不保证迭代顺序;rehash 会使迭代器失效。

std::map 是有序关联容器,标准保证查找、插入和删除为 O(log n),常见实现是红黑树,但具体树型仍属于实现细节。需要有序遍历、范围查询、稳定的最坏复杂度或频繁使用 lower_bound 时选 map;追求平均查找吞吐且有可靠哈希函数时选 unordered_map。还要比较内存开销、缓存局部性、key 分布、迭代器失效和拒绝服务场景,不能只背复杂度表。


207. C++ 的 new/deletemalloc/free 有什么区别?对象构造和分配器如何衔接?

来源:沐曦 AI Infra 一面太初 AI Infra 一面文远知行 AI Infra 一面

new T(args...) 是 C++ 表达式:先调用相应的 operator new 获得原始存储,再在其中构造对象;delete 先调用析构函数,再把存储交给 operator delete。普通 new 失败默认抛出 std::bad_allocnew (std::nothrow) 才返回空指针。malloc 只按字节分配原始内存并返回 void*,失败返回空指针,不知道类型,也不会调用构造或析构函数。

分配和释放必须成对:new/deletenew[]/delete[]malloc/free 不能混用,否则行为未定义。Placement new 只在给定地址构造对象,调用方仍负责显式析构和底层存储释放。工程代码优先用值语义、容器和 RAII;确需定制时再使用 allocator、内存池或重载 operator new,并用 sanitizer 检查越界、重复释放和 use-after-free。


208. C++ 智能指针如何管理所有权?控制块、数组和循环引用有哪些边界?

来源:沐曦 AI Infra 一面太初 AI Infra 一面

unique_ptr 表示独占所有权,只能移动,通常只保存指针和删除器;shared_ptr 让多个句柄共享对象,其控制块维护强引用、弱引用和删除器等状态;weak_ptr 不增加强引用,可用 lock() 临时取得有效的 shared_ptr,用于打破双向图中的循环引用。make_shared 常把对象和控制块一次分配,减少分配次数,但对象内存要等弱引用也释放后才完全归还。

连续数组应使用 unique_ptr<T[]>、标准库支持的 shared_ptr<T[]> 或更优先的 std::vector<T>,不能让普通 unique_ptr<T> 用错误的 delete 释放 new T[n]。管理文件、socket、GPU buffer 等非 new 资源时要提供匹配删除器。引用计数的同步只保护控制块生命周期,不自动保护被指对象;多个线程修改对象或同一个智能指针变量仍需同步。


209. C++ 多态和虚函数通常如何实现?构造、析构期间调用虚函数会怎样?

来源:飞腾 AI Infra 二面沐曦 AI Infra 一面太初 AI Infra 一面

C++ 的函数重载、模板和 CRTP 属于编译期多态;通过基类指针或引用调用虚函数属于运行期多态。常见 ABI 为含虚函数的对象保存 vptr,指向记录虚函数入口的 vtable,调用时按动态类型间接分派;但标准只规定可观察行为,不强制虚表的具体内存布局。多态基类若可能通过基类指针删除派生对象,析构函数通常必须是 virtual。

在基类构造期间,派生部分尚未构造;在基类析构期间,派生部分已经销毁,因此虚调用只分派到当前正在构造或析构的类,不会调用更派生类覆盖。依赖派生状态会破坏对象生命周期不变量,调用纯虚函数还可能导致未定义行为或链接/运行时错误。工程上应避免在构造和析构函数中依赖虚分派,改用构造后初始化、工厂函数或非虚私有初始化步骤。


210. C++ lambda 的捕获、闭包对象和生命周期风险是什么?

来源:沐曦 AI Infra 一面

lambda 表达式会生成一个匿名闭包类型,对捕获变量形成数据成员,并以 operator() 执行函数体。[=][&] 分别默认按值和按引用捕获,显式捕获更容易审计;按值捕获默认不能修改副本,加入 mutable 后可以修改闭包内部状态。初始化捕获可移动资源,泛型 lambda 的 auto 参数则让调用运算符成为模板。

最大风险是生命周期:按引用捕获的局部变量离开作用域后会悬空,异步任务捕获 this 也可能在对象销毁后访问无效地址。可以捕获值、shared_ptr,或捕获 weak_ptr 后在执行时检查存活状态。无捕获 lambda 可转换为函数指针;std::function 能类型擦除不同可调用对象,但可能引入间接调用、复制和动态分配开销,热点路径应以基准决定是否使用模板回调。


211. C++ 四种 Cast 有什么区别?父类向子类转换何时安全?

来源:小鹏汽车 AI Infra 一面蔚来汽车 AI Infra 面经

static_cast 用于编译期可检查的数值转换、上行转换及开发者能证明安全的下行转换;它不验证对象运行时真实类型。dynamic_cast 借助 RTTI 在多态继承层次中检查转换,指针失败返回空,引用失败抛 std::bad_castconst_cast 只改变 cv 限定,若底层对象本来就是 const,再通过结果写入仍是未定义行为;reinterpret_cast 进行低层表示转换,不能凭转换本身建立对象、对齐或别名访问的合法性。

父类指针向子类转换只有在对象真实动态类型确实是目标子类或其派生类时才可安全使用。类型不确定时用 dynamic_cast 或重新设计接口;static_cast 下行只适合已有外部不变量保证的性能敏感边界。多继承下指针值还可能需要调整,因此不能用 C 风格强转或假设基类子对象总在对象起始地址。


212. 动态链接库如何解析依赖和符号?依赖顺序为什么可能导致链接或加载失败?

来源:小鹏汽车 AI Infra 一面

编译器先生成目标文件,其中可以保留未解析符号;链接器再从目标文件、静态库和共享库中解析引用并生成可执行文件。静态归档常按命令行从左到右扫描,只抽取当时能解决未定义符号的成员,因此依赖方通常放在被依赖库之前,循环依赖需要重复列出、使用 group 选项或消除结构问题。--as-needed 等选项也会让看似无用的共享库不进入依赖表。

运行时加载器依据记录的动态依赖、SONAME 和受控搜索路径装载共享对象,再完成重定位和符号绑定。常见失败包括库文件找不到、ABI/版本不兼容、符号被隐藏、依赖未声明及同名符号覆盖。排障使用链接器 map、readelfobjdumpldd 或加载器调试输出;不要把静态库顺序规则笼统套到所有平台,也不要依赖不受控的全局环境搜索路径修复生产部署。


213. C/C++ 回调有哪些实现方式?如何管理上下文、生命周期和异常边界?

来源:科大讯飞 AI Infra 面经

C 接口常用函数指针加 void* context,由回调把 context 转回业务对象;C++ 还可用成员函数适配器、函数对象、lambda、模板可调用参数和 std::function。模板或具体闭包类型有利于内联,std::function 便于统一存储不同回调,但类型擦除可能带来间接调用、复制和堆分配。成员函数还需要对象实例,不能直接当作普通 C 函数指针。

异步回调的重点不是语法,而是所有权:注册方与执行方要约定 context 谁持有、何时注销、取消后是否仍可能在途执行。跨线程访问共享状态需要同步,捕获引用或 this 必须保证生命周期;跨 C ABI 边界不能让 C++ 异常逃逸,应在边界捕获并转成错误码。还要防止回调重入、注销与执行竞态及在持锁状态下调用未知业务代码。


214. C/C++ 从预处理到链接经历哪些阶段?每阶段常见错误如何定位?

来源:飞腾 AI Infra 二面

预处理阶段展开宏、处理条件编译并包含头文件;编译阶段完成词法语法、类型检查和优化,生成汇编或中间表示;汇编阶段把汇编转成含符号表与重定位信息的目标文件;链接阶段合并目标文件和库、解析符号与重定位,生成可执行文件或共享库。实际工具链可能把多个阶段合并执行,但职责仍可区分。

宏展开、缺头文件通常在预处理阶段暴露;类型和模板实例化问题多为编译错误;非法指令或汇编语法属于汇编阶段;undefined reference、重复定义和 ABI 不兼容属于链接问题。定位时先保留完整命令行和第一处错误,再用 -E-S-c 分阶段输出,结合 nm/readelf/objdump 检查符号,不要把所有构建失败都归为“编译器报错”。


215. 单例模式和工厂模式分别解决什么问题?在 C++ 中如何避免生命周期陷阱?

来源:飞腾 AI Infra 二面

单例约束某类在指定作用域内只有一个实例并提供访问点,但它会引入全局状态、隐藏依赖和测试隔离困难。C++11 起函数内静态对象的初始化具备线程安全保证,Meyers Singleton 比手写双重检查更稳妥;仍要处理析构顺序、动态库卸载和实例内部状态的并发安全,单例“创建安全”不等于业务方法线程安全。

工厂把“选择并构造具体实现”从调用方抽离:简单工厂集中分支,工厂方法把创建推迟给子类或策略,抽象工厂创建一组相互匹配的对象。它适合实现可替换、构造复杂或需要插件注册的场景,但产品很少时会徒增层次。工程上优先显式依赖注入和 RAII 返回值,例如 unique_ptr<Interface>,避免工厂偷偷持有全局单例并制造不清晰的销毁顺序。


216. CPython 的引用计数、循环垃圾回收和对象生命周期如何工作?

来源:爱奇艺 AI 平台研发 Infra 面经

CPython 主要通过引用计数管理对象:强引用增加计数,引用释放后计数减少,降到零时对象通常立即进入销毁流程。这使多数对象的回收时机较可预测,但彼此强引用的容器可能形成环,即使外部已不可达,计数仍不为零;CPython 因此还使用分代的循环垃圾回收器追踪可形成引用环的容器并识别不可达环。

del x 只删除一个名字绑定,不保证对象立即销毁;弱引用不会维持对象存活,适合缓存和观察者关系。带终结逻辑的对象、跨线程引用和 C 扩展会让生命周期更复杂,具体代数和调度策略也可能随 CPython 版本变化。文件、锁、数据库连接等外部资源不应依赖 GC 时机,应使用 withtry/finally 或显式 close() 管理。


217. Python 为什么通常比编译型语言慢?如何定位并选择优化路径?

来源:爱奇艺 AI 平台研发 Infra 面经

以常见 CPython 执行为例,动态类型需要在运行时解析对象和操作,数值通常是带元数据的对象,字节码解释分派、引用计数和间接访问也增加指令与内存开销;对象布局分散还会影响缓存局部性。GIL 会限制单进程纯 Python CPU 密集线程的并行扩展,但它不是单线程代码慢的唯一原因,也不能据此断言所有 Python 实现或所有负载都慢。

优化先用 profiler 找到真实热点,再依次考虑更好的算法和数据结构、减少 Python 层循环、批量化/向量化、缓存和降低对象分配。仍不足时可把热点交给 NumPy 等原生库、C/C++/Rust 扩展、JIT 或独立计算服务;CPU 并行可评估多进程,I/O 并发可用线程或异步。选择要用目标数据和端到端基准验证,不能为了局部微基准牺牲可维护性和序列化成本。


218. etcd 与 Redis 的索引和存储结构有什么差异?设计目标为什么不同?

来源:百度 AI Infra 校招面经

以典型 etcd v3 实现为例,键值更新形成带全局 revision 的 MVCC 版本;内存索引把 key 映射到版本信息,持久化后端保存 revision 对应的数据,写入还要经过 Raft 复制和提交。这套结构服务于线性一致读写、事务比较、按 key 范围查询和从指定 revision 开始的 watch,历史版本会通过 compaction 控制。具体 B-tree 与后端实现属于版本细节,回答时应区分逻辑契约和当前实现。

Redis 的顶层 keyspace 通常使用哈希字典,单 key 的值再按 String、Hash、ZSet、Stream 等类型采用不同编码和数据结构,并通过 RDB/AOF 与复制提供持久化和高可用选项。它优先追求低延迟和丰富数据操作,不默认提供 etcd 的 Raft 线性一致语义;etcd 也不是通用缓存。比较两者不能简化成“B+ 树对哈希表”,应从一致性、版本/watch、查询模型、数据规模、延迟和故障语义选择。


219. CallableFutureCompletableFuture 有什么区别?如何等待多个任务全部完成?

来源:影石 Java 后端一面,2026 年 8 月 11 日

Callable<V> 描述一个可返回结果并抛出受检异常的任务;提交给 ExecutorService 后得到 Future<V>,后者表示异步结果,可查询状态、取消或通过 get() 等待。Future 的组合能力较弱;CompletableFuture 同时是结果容器和异步阶段,支持串行、并行、异常恢复与 allOf/anyOf 组合,但默认线程池和阻塞调用必须显式治理。

等待全部任务可用 invokeAllCompletableFuture.allOfCountDownLatch。选择取决于是否需要返回值、动态组合和超时取消。不能在同一个容量不足的线程池中让父任务阻塞等待其子任务,否则可能线程饥饿死锁;生产代码还要传播 Deadline、处理部分失败并回收未完成任务。


220. Java 反射如何工作?反射为什么可能破坏单例,应该怎样防护?

来源:影石 Java 后端一面,2026 年 8 月 11 日

反射通过 Class 元数据在运行时检查构造器、字段、方法和注解,并可在权限允许时动态创建对象或调用成员。它支撑依赖注入、序列化和测试框架,但会弱化编译期约束,带来访问控制、可维护性和一定调用开销;模块强封装环境下,深反射还可能被拒绝。

私有构造器、双重检查锁或静态内部类只控制普通创建路径,若构造器被反射调用仍可能产生第二个实例。可在构造器中检测重复初始化,但要处理并发和反序列化;更稳妥的是使用 enum 单例,JVM 禁止反射创建枚举实例,并天然处理序列化。单例实例唯一不代表内部可变状态线程安全。


221. InnoDB 的聚簇索引和二级索引分别存什么?主键与普通索引查询如何回表?

来源:影石 Java 后端一面,2026 年 8 月 11 日

InnoDB 的聚簇索引叶子节点保存整行数据,一张表只有一个聚簇组织顺序,通常由主键承担;没有合适主键时会选择可用唯一键或生成隐藏行 ID。二级索引叶子保存索引列和主键值,因此通过普通电话号码索引查询未覆盖字段时,要先取得主键,再访问聚簇索引完成回表。

按主键 id 查询直接走聚簇索引;按电话索引查询走二级索引。若查询字段都在二级索引中,可使用覆盖索引避免回表。索引是否实际采用还取决于选择性、统计信息、条件写法和成本估算,不能只看“建了索引”。


222. InnoDB 中普通 SELECT、锁定读和 UPDATE 分别会加什么锁?范围条件为什么影响插入?

来源:影石 Java 后端一面,2026 年 8 月 11 日;小红书/百度 Agent 开发实习一面

普通一致性 SELECT 在常见 MVCC 场景下读取快照,通常不加记录锁;SELECT ... FOR UPDATE/SHAREUPDATE/DELETE 属于当前读。使用唯一索引等值命中时通常锁定目标记录;非唯一索引或范围扫描在可重复读下可能使用 Record、Gap 或 Next-Key Lock,以锁住扫描到的索引范围并抑制幻读。

锁定范围由隔离级别、执行计划、索引、边界是否命中和实际扫描路径共同决定。例如条件落在已有键 3 与 5 之间,即使没有匹配行,也可能锁住对应间隙并阻止插入 4。回答前应说明表结构和索引,再用 EXPLAIN、事务隔离级别及 performance_schema.data_locks 验证,不能只从 SQL 文本猜锁。


223. CDC 分别从 MySQL 和 PostgreSQL 的什么日志读取变更?如何保证快照与增量衔接?

来源:影石 Java 后端一面,2026 年 8 月 11 日

MySQL CDC 通常读取 binlog,Row 格式可提供行级变更,并通过 file/position 或 GTID 维护位点。PostgreSQL 底层写 WAL,CDC 通常通过 logical decoding 和 replication slot 把 WAL 解码为逻辑变更;WAL 本身服务崩溃恢复,不能把“直接解析 WAL 文件”和稳定的逻辑复制协议混为一谈。

全量初始化要在一致性快照边界记录增量位点,再回放该点之后的日志;消费侧按事件 ID、表主键和版本幂等,处理事务边界、DDL、心跳、乱序和 Schema 演进。PostgreSQL slot 或 MySQL 日志长期积压会占用存储,必须监控 lag、保留期和下游背压。


224. Spring 的 IoC 与 DI 是什么?Bean 可以通过哪些方式注册和注入?

来源:影石 Java 后端一面,2026 年 8 月 11 日

IoC 表示对象创建、依赖装配和生命周期从业务代码交给容器;DI 是实现 IoC 的主要方式。Bean 可通过组件扫描、@Bean 工厂方法、@Import、XML 或程序化 registerBean 注册。依赖可用构造器、Setter 或字段注入;构造器注入能表达必需依赖、支持不可变对象和测试,通常应优先。

容器外手动 new 的对象不会自动经历依赖注入、AOP 和完整 Bean 生命周期。确需接管现有对象时可使用受控的 BeanFactory API,但更好的做法是把创建边界放回容器。回答“如何创建 Bean”时要区分注册 BeanDefinition、实例化对象和依赖注入三个阶段。


225. Redis 的 RDB、AOF 和混合持久化分别如何工作?线上怎样选?

来源:京东后端开发一面,2026 年 8 月 20 日

RDB 在指定时机生成数据快照,文件紧凑、恢复快,适合备份和全量恢复,但可能丢失最近一次快照后的写入;生成快照通常依赖 fork 与 Copy-on-Write,大内存和高写入下要关注 fork 延迟与额外内存。AOF 追加写命令并按 appendfsync 策略刷盘,数据丢失窗口更小,但文件和恢复成本通常更高,并需要后台重写压缩历史。

混合持久化在 AOF 重写时使用 RDB 前缀加后续增量,兼顾恢复速度和数据新鲜度。线上选型先定义 RPO、RTO、磁盘与 fork 预算,并结合复制、哨兵/集群和异地备份;开启两者也不等于不会丢数据,仍要演练崩溃恢复并验证持久化文件。


226. 网络安全中的四层与七层防御有什么区别?SQL 注入为什么属于应用层攻击?

来源:影石 Java 后端二面,2026 年 8 月 11 日

四层防御主要依据 IP、端口、协议和连接状态处理流量,例如 SYN Flood 防护、连接限速和四层负载均衡;七层防御理解 HTTP、RPC 等应用协议和业务语义,可检查 URL、Header、Body、身份与调用行为。层级越高信息越丰富,但解析成本、误报和被绕过的复杂度也更高。

SQL 注入发生在应用把不可信输入拼成 SQL 语义的边界,因此属于应用层问题。根治手段是参数化查询、最小数据库权限、输入约束和安全 ORM 用法;WAF 可拦截已知模式,但不能替代代码修复。四层设备看不到解密后的 SQL 语义,也无法单独解决注入。


227. Nacos 的注册发现链路是什么?它在 CAP 中到底属于 CP 还是 AP?

来源:影石 Java 后端二面,2026 年 8 月 11 日

服务实例向 Nacos 注册服务名、分组、集群、地址和元数据,客户端通过查询、订阅和本地缓存获得实例列表,再结合负载均衡发起调用。临时实例通常依赖心跳或连接状态维持,失联后被摘除;客户端还要处理推送丢失、服务端不可用和缓存过期,不能把注册中心当成永不失败的实时真相。

“Nacos 属于 CP 还是 AP”不能只背一个字母。不同版本和数据类型可采用不同一致性路径:临时服务实例更偏可用性的 Distro/AP 语义,持久实例或配置数据可能使用 Raft/CP 语义。回答时应锁定 Nacos 版本、实例类型和业务数据,再说明网络分区时希望保可用还是保强一致。


228. TCP 已建立连接后,一端突然掉电或断网,另一端的连接状态会怎样变化?

来源:字节 AML / 火山方舟 AI Infra 一面,2026 年 8 月 26 日

掉电一端来不及发送 FIN 或 RST,另一端内核不会凭空知道对端消失,连接可能继续保持 ESTABLISHED。若应用继续发送数据,TCP 会重传并按系统重试策略最终超时;若连接长期完全空闲且未启用探测,它可能保持很久。对端重启后旧连接状态已经不存在,收到旧连接报文时通常会返回 RST。

生产系统不能依赖默认 TCP 超时及时发现故障。应设置应用请求 Deadline、读写超时和业务心跳,必要时启用并调优 TCP Keepalive;连接池发现超时或 RST 后移除连接并按幂等语义重连。Keepalive 默认周期往往很长,而且只能证明网络路径和对端协议栈存活,不能证明应用线程健康,因此应用层健康语义仍不可少。


229. Python 高阶函数、闭包与回调中的参数和生命周期如何流转?

来源:大方云图研发实习一面

Python 函数本身是对象,可以作为参数传入、作为返回值返回,实例也可通过 __call__ 变成可调用对象。调用高阶函数时,传入的是函数对象引用;包装器收集 *args/**kwargs 后再次调用它,Python 再按位置参数、关键字参数和默认值把实参绑定到形参。Python 调用表达式定义了绑定规则,数据模型则描述了函数、绑定方法和 callable 对象。

闭包保存的是自由变量所在的 cell,而不是创建闭包那一刻的值副本,因此循环里创建 lambda 容易出现 late binding;可通过默认参数或局部工厂冻结当轮值。绑定方法会持有实例,回调注册表也可能延长对象生命周期,注销不及时会造成资源泄漏。异步回调还要处理异常、取消、超时和调用方已销毁等边界,不能只会把函数当参数传进去。


230. Celery 在异步任务平台中负责什么?Broker、Worker、Result Backend、Retry 与幂等如何协作?

来源:大方云图研发实习一面

生产者把任务消息发到 Broker,Worker 消费并执行;Result Backend 是可选的状态/结果存储,不等于 Broker。Celery 负责分发、路由、重试和任务状态抽象,但数据库事务、业务幂等和 exactly-once 仍由应用保证。Celery Tasks 文档也将 retry、ack 和 result backend 作为不同机制。

默认早确认降低重复执行概率,但 Worker 确认后宕机会丢失任务;acks_late 把确认推迟到执行后,恢复能力更强,却可能重复执行。Celery 还有一个容易漏掉的边界:执行任务的子进程被信号终止或异常退出时,即使开启 acks_late,默认仍可能确认消息;只有明确评估并启用 task_reject_on_worker_lost 才要求这类消息重投,而它也可能制造持续失败的重投循环。任务应携带稳定业务幂等键,按异常类型决定 retry,设置指数退避、上限和 Dead Letter 路径。结果不需要查询时不要无条件持久化;需要状态时明确过期和清理,否则 Result Backend 会成为隐性存储瓶颈。


231. MyBatis 接入项目需要配置哪些组件?Mapper、映射、TypeHandler、事务和缓存如何分工?

来源:小公司 AI Agent 开发一面

MyBatis 的核心入口是 SqlSessionFactory,底层环境包含 DataSource 和事务管理;Mapper 接口与 XML/注解中的 statement 定义 SQL 和参数/结果映射,ResultMap 解决列到对象的复杂映射,TypeHandler 负责 JDBC 类型与 Java 类型转换。插件、别名、数据库厂商选择和 Mapper 注册都属于配置层。MyBatis 官方配置结构明确列出了这些组件。

一级缓存属于 SqlSession,二级缓存绑定 Mapper namespace,不能把它当通用分布式缓存;写操作、事务边界和脏数据风险要一起评估。接入 Spring 后事务通常由 Spring 模块接管,不应再假设 mybatis-config 中的 TransactionManager 决定最终事务语义。排查时区分“Mapper 未注册”“statement ID/namespace 不匹配”“参数映射错误”和“事务未提交”,不要都归为 SQL 写错。


232. useEffect 何时运行和清理?依赖数组、闭包陈旧、竞态与 Strict Mode 重放如何处理?

来源:拼多多 AI 全栈两轮技术面

useEffect 用来把组件与外部系统同步。组件提交后执行 setup;依赖变化时先用旧值执行 cleanup,再用新值执行 setup;卸载时最后清理一次。省略依赖数组会在每次 commit 后运行,空数组表示不因响应式值变化而重跑,但开发环境的 Strict Mode 会额外执行一次 setup→cleanup→setup,用来暴露清理不完整的问题。这些语义以 React 官方 useEffect 文档为准。

依赖项必须覆盖 effect 使用的所有响应式值,不能为了“只跑一次”故意漏写。函数/对象每次渲染都创建会导致重复执行;异步请求则要在 cleanup 中取消,或用 ignore/generation 标志拒绝晚到结果,防止旧请求覆盖新状态。若逻辑不在同步外部系统,通常不需要 Effect,应在事件处理或渲染计算中完成。


233. AI 对话流式接口如何约定事件、错误和重放?前端如何增量解析并避免频繁重渲染?

来源:拼多多 AI 全栈两轮技术面

协议先定义事件 envelope,例如 run_id/event_id/seq/type/payload/error/final,区分 token、Tool 状态、Artifact、错误和结束事件。SSE 原生支持 eventdataid 和重连时间;id 可配合持久事件日志做断线续传,MDN SSE 文档说明了这些字段。需要 POST、自定义 Header 或二进制流时,可用 Fetch 的 ReadableStream,但重连和事件 framing 要自行实现。

网络 chunk 不等于一个完整 JSON。前端用流式 TextDecoder 处理 UTF-8 边界,按协议分隔符组帧后再解析,并用 seq 去重、检测缺口。渲染层把短时间内的 token 合并后按 animation frame 或固定间隔刷新,Tool/错误/final 事件立即提交,避免每个 token 都触发整棵消息树重渲染。取消要传播到服务端任务;重连只重放尚未确认的事件,不能重新执行已有副作用。


234. 浏览器强缓存与协商缓存如何工作?Cache-ControlETagLast-Modified 与 CDN 如何协作?

来源:拼多多 AI 全栈两轮技术面

“强缓存”通常指响应仍新鲜时直接复用本地副本而不联系源站,主要由 Cache-Control: max-age/s-maxageExpires 和缓存键决定;“协商缓存”实质是过期后发条件请求,用 ETag + If-None-MatchLast-Modified + If-Modified-Since 验证,未变化返回 304 并复用正文。RFC 9111定义了新鲜度、重验证和缓存指令,ETag 通常比秒级时间戳更适合精确验证。

带内容哈希的 JS/CSS/图片可设置长 max-age, immutable;HTML 或配置入口通常要求重验证;用户私有或敏感响应使用 private/no-store 并把身份维度纳入缓存键。CDN 属共享缓存,可用 s-maxage 独立控制边缘新鲜度,还要正确设置 Vary,否则压缩格式、语言或身份响应可能串用。更新策略必须与文件名版本化和失效机制配套,不能只把过期时间设得很长。


算法与手撕题单

以下题目来自同一时间窗口,适合单独放入算法训练计划:

题目 来源
最大子数组和 拼多多提前批一面,7 月 30 日;字节数据平台 Agent 一面
无重复字符的最长子串 百度秋招后端一面,7 月 30 日;百度 AI Infra 一面;腾讯 CDG AI Infra 框架侧一面
二叉树前序遍历、层序遍历 知乎后端一面,8 月 4 日;小红书数据库智能化一面,8 月 10 日;视频 b 二面;阿里实习 AI Infra 面经百度大模型研发一面
两两交换链表节点 拼多多服务端三面,8 月 4 日;百度内容营销与广告一面,8 月 12 日
奇偶链表 字节后端社招一面,8 月 6 日
最长有效括号 字节后端社招二面,8 月 6 日;虾皮 AI Infra 二面百度 Coding Agent 三面美团 Agent 一面
数组第 K 大元素 字节后端社招三面,8 月 6 日;快手 AI 全栈一面
删除重复字符并保持字典序最小 字节 Agent 开发一面,8 月 9 日
有效括号字符串、最长递增子序列 小红书数据库智能化面经,8 月 10 日;小红书/百度 Agent 开发实习一面
SQL:查询平均工资最高部门的管理者 快手测开一面,8 月 12 日
最多 K 个重复元素的最长子数组(LC 2958) 阿里云 AI 全栈开发一面
限制最大并发数为 3,批量调用外部接口 快手 Agent 开发一面
将扁平 List 转换为树形 JSON 字节跳动 AI Agent 开发一面
合并两个有序数组 影石创新 AI Agent 一面;多益三面
LRU Cache 懂车帝 Agent 开发一面;阶跃星辰 AI Infra 实习面经快手 AI Infra 校招面经
二叉树锯齿形层序遍历 字节跳动 AI Agent 开发一面
手写多头注意力(MHA) 小鹏 VLA 大模型算法工程师一面
顺时针旋转矩阵 90° 小鹏 VLA 大模型算法工程师一面;蔚来 AI Infra 一面(Python)
查找会议静默区间及多人重叠区间 字节跳动 AI Agent 一面
判断链表是否有环 视频 b 二面
实现平方根函数 文库相关岗位一面
单链表去重 阿里云 AI Infra 一面,4 月 13 日
拓扑排序 / 课程表 II(输出可行顺序)、二叉树中序遍历、满二叉树性质 三星 AI Infra 实习一面,5 月 1 日;字节 Agent 后端终面
最长无重复元素子数组 百度 AI Infra 提前批一面,4 月 13 日
买卖股票的最佳时机 I / II 抖音搜推 AI Infra 一面,5 月 1 日;快手 AI Infra 面经(版本未注明)
冒泡、快速、归并、堆排序(含与标准 sort 对拍) 阿里云 Agent Infra 一面,4 月 26 日;AI Infra 春招面经,3 月 25 日;小马智行 AI Infra 实习面经(归并排序)字节 AI Infra 二面(堆排序)百川智能医疗大模型后训练一面(冒泡排序与对拍)
柱状图接雨水 百度 AI Infra 暑期一面,5 月 1 日;阿里校招 AI Infra 一面
多线程交替打印(1/2、A1B2C3,含线程退出协调) 阿里云 Agent Infra 一面,4 月 26 日;百度 Coding Agent 三面
模拟死锁 虾皮 AI Infra 后端一面,4 月 13 日
手写 MHA(含 mask 与数值稳定性) 小鹏 AI Infra 一面,4 月 13 日(付费截断来源,仅保留公开题干)
岛屿数量(LC 200) 蔚来 AI Infra 二面
按层交替方向旋转矩阵外圈一格 多公司 Infra 面经
反转单链表 / 反转链表 II(LC 206/92) 多公司 Infra 面经(LC 92)快手 AI 应用开发一面虾皮 Agent 开发一面
区间排序:合并区间(LC 56)/最多不重叠区间 阶跃星辰 AI Infra 实习面经(区间调度)快手大模型应用算法一面(合并区间)
K 个一组翻转链表(LC 25) 美团北斗 AI Infra 校招面经
Pow(x, n),计算数值的整数次方(LC 50) 字节 AI Infra 实习面经
合并两个或 K 个升序链表(LC 21/23) 百度 AI Infra 实习面经(LC 23)美团 AI Infra 实习面经(LC 23)美团 Agent 一面(LC 21)
二叉树右视图(LC 199) 百度 AI Infra 实习面经
阶乘末尾零的个数(LC 172) 快手 AI Infra 校招面经
数组中连续相同数字的最大出现次数 字节 AI Infra 实习面经
10 点环上走 N 步回到原点的方案数 字节 AI Infra 后端面经
0-1 背包与完全背包 蔚来 AI Infra 面经
两数之和(LC 1) 沐曦 AI Infra 一面字节 AML / 火山方舟 AI Infra 一面(数组可重复、元素不可复用、复杂度与原地排序追问)
矩阵第 K 小元素(原帖有序约束未完整记录) 腾讯 CDG AI Infra 框架侧面经
爬楼梯(递归、记忆化与动态规划,LC 70) 字节火山引擎 Managed Agent 一面,2026 年 8 月 13 日
最近 T 秒访问日志:按用户最后访问时间降序返回 百度 Coding Agent 一面
统计包含数组全部不同值的连续子数组数量 拼多多大模型算法一面
递归合并嵌套 JSON(dict/list/数值/字符串/布尔规则,且不修改输入) 字节 Agent 开发一面
最长回文子串(LC 5) 字节数据平台 Agent 一面百度 AI 测开一面
最长公共前缀 元石科技后端/Agent 一面
大数乘法 腾讯 WXG 微信读书一面
URL 查询参数解析为 Map 大方云图研发实习一面
两个栈实现队列,并追问线程安全 字节 Agent 开发一面
所有连续数字子串的十进制数值之和 京东 8 月 22 日笔试
手写防抖函数(leading/trailing、取消、参数与异步竞态) 拼多多 AI 全栈两轮技术面

下一篇建议继续看: