moklgy's blog moklgy's blog
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • 后端文章

    • 技术题
  • .netcore

    • 《asp.netcore》笔记
    • 《设计模式》
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

moklgy docs

全栈初级开发工程师
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • 后端文章

    • 技术题
  • .netcore

    • 《asp.netcore》笔记
    • 《设计模式》
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础
  • 补充
  • 中级
    • 🟡 中级梯队:C# 高级特性与内存管理(1–30)
      • 1)async/await 状态机详解
      • 2)ConfigureAwait(false) 深度
      • 3)Task.Run vs Task.Factory.StartNew
      • 4)CancellationToken 最佳实践
      • 5)SynchronizationContext 深度
      • 6)GC 深度:分代与提升
      • 7)Span 与 Memory 深度
      • 8)ArrayPool 实战
      • 9)weak reference(弱引用)
      • 10)finalizer 与 GC 压力
      • 11)record 类型深度
      • 12)Primary Constructor(主构造函数)
      • 13)Nullable Reference Types 深度
      • 14)Pattern Matching 深度
      • 15)Expression Tree(表达式树)实战
      • 16)Reflection 性能坑
      • 17)IDisposable 模式完整版
      • 18)Interlocked 与原子操作
      • 19)volatile 关键字深度
      • 20)ThreadLocal 场景
      • 21)线程池调优
      • 22)Parallel.ForEach 深度
      • 23)Channels (System.Threading.Channels)
      • 24)Task vs Thread
      • 25)SemaphoreSlim 与限流
      • 26)lock vs Monitor vs Mutex
      • 27)Lazy 线程安全模式
      • 28)ImmutableCollections 价值
      • 29)StringPool / Intern
      • 30)Encoding 与字符集
    • 🟡 中级梯队:ASP.NET Core 高级特性与性能调优(31–60)[1]
      • 31)中间件短路与条件分支
      • 32)IApplicationBuilder 管道构建原理
      • 33)Endpoint Routing 深度
      • 34)Filter 执行顺序与作用域
      • 35)全局异常处理:Middleware vs Filter
      • 36)模型绑定深度
      • 37)模型验证与 FluentValidation
      • 38)IOptions 模式深度
      • 39)配置源优先级与覆盖
      • 40)日志结构化与 Serilog
      • 41)Kestrel 性能调优
      • 42)响应压缩
      • 43)CORS 深度
      • 44)健康检查(Health Checks)
      • 45)静态文件与 CDN
      • 46)SignalR 连接管理
      • 47)授权策略(Policy-Based Authorization)
      • 48)JWT 刷新令牌(Refresh Token)
      • 49)Rate Limiting(限流)
      • 50)输出缓存(Output Caching)
      • 51)后台任务(Hosted Service)
      • 52)gRPC 深度
      • 53)Minimal API vs Controller
      • 54)HttpClient 工厂模式
      • 55)请求/响应压缩与流式传输
      • 56)API 版本控制
      • 57)请求日志与 TraceId
      • 58)内存缓存(IMemoryCache)深度
      • 59)分布式缓存(IDistributedCache)
      • 60)WebSockets 生命周期管理
    • 🟡 中级梯队:EF Core 性能、数据库锁与高级查询(61–90)[1]
      • 61)N+1 进阶与 SplitQuery
      • 62)EF Core 批量操作(Bulk Update/Delete)
      • 63)乐观锁与并发令牌
      • 64)悲观锁与事务隔离级别
      • 65)连接池耗尽(Connection Pool Exhaustion)
      • 66)SQL 注入进阶:动态排序
      • 67)死锁(Deadlock)分析
      • 68)索引失效深度
      • 69)执行计划(Execution Plan)
      • 70)复杂查询:Group By 优化
      • 71)临时表 vs 表变量 vs CTE
      • 72)TVP(表值参数)
      • 73)分库分表策略
      • 74)数据库游标(Cursor)
      • 75)Window Function(窗口函数)
      • 76)JSON 数据存储
      • 77)CDC(Change Data Capture)
      • 78)数据库备份与恢复模型
      • 79)EF Core 拦截器(Interceptor)
      • 80)Compiled Query(编译查询)
      • 81)Shadow Property 实战
      • 82)Value Conversion(值转换)
      • 83)Owned Entity Type(从属实体)
      • 84)Global Query Filter 进阶
      • 85)DbContext Pooling
      • 86)EF Core 8 Primitive Collections
      • 87)HierarchyId 与树形结构
      • 88)Spatial Data(空间数据)
      • 89)In-Memory Database 坑
      • 90)Raw SQL 与 Dapper 混用
    • 🟡 中级梯队:设计模式进阶与架构模式(91–120)[1]
      • 91)单例模式的线程安全与破坏
      • 92)简单工厂 vs 工厂方法 vs 抽象工厂
      • 93)代理模式 vs 装饰器模式
      • 94)适配器模式(Adapter)实战
      • 95)观察者模式 vs 发布订阅
      • 96)策略模式与工厂结合
      • 97)模板方法模式(Template Method)
      • 98)责任链模式(Chain of Responsibility)
      • 99)建造者模式(Builder)
      • 100)原型模式(Prototype)与深拷贝
      • 101)享元模式(Flyweight)
      • 102)组合模式(Composite)
      • 103)命令模式(Command)
      • 104)状态模式(State)
      • 105)中介者模式(Mediator)
      • 106)迭代器模式(Iterator)与 yield
      • 107)备忘录模式(Memento)
      • 108)访问者模式(Visitor)
      • 109)桥接模式(Bridge)
      • 110)解释器模式(Interpreter)
      • 111)贫血模型 vs 充血模型(DDD 基础)
      • 112)实体 vs 值对象(Value Object)
      • 113)聚合根(Aggregate Root)设计
      • 114)仓储模式(Repository)
      • 115)工作单元(Unit of Work)
      • 116)领域事件(Domain Event)
      • 117)领域服务(Domain Service)
      • 118)CQRS(命令查询职责分离)
      • 119)六边形架构 / 洋葱架构
      • 120)微服务拆分原则
    • 🟡 中级梯队:Redis 高级特性与分布式锁深度(121–150)[1]
      • 121)Redis 线程模型深度
      • 122)Bitmap 与 HyperLogLog
      • 123)GeoHash 原理与应用
      • 124)Bloom Filter(布隆过滤器)插件
      • 125)Stream(消息队列)
      • 126)Lua 脚本的原子性与限制
      • 127)Keys 淘汰策略深度
      • 128)BigKey 治理
      • 129)HotKey(热点 Key)治理
      • 130)RDB vs AOF 灾难恢复实战
      • 131)主从复制(Replication)原理
      • 132)Sentinel(哨兵)选主逻辑
      • 133)Cluster 重定向与 Hash Tag
      • 134)Client-side Caching(客户端缓存)
      • 135)Redis 事务(ACID 假象)
      • 136)分布式锁:Redlock 争议
      • 137)分布式锁:可重入性
      • 138)分布式锁:惊群效应
      • 139)Redisson 原理深度
      • 140)Redis 内存碎片
      • 141)Redis 慢查询日志
      • 142)Redis 协议(RESP)
      • 143)缓存预热
      • 144)多级缓存架构
      • 145)Redis 运维命令
      • 146)Codis vs Redis Cluster
      • 147)Redis 作为数据库的坑
      • 148)List 作为消息队列的坑
      • 149)Redis 脑裂与数据丢失
      • 150)Redis 模块开发(ReModule)
    • 🟡 中级梯队:RabbitMQ 可靠性与性能调优(151–180)[1]
      • 151)RabbitMQ 架构模式:镜像队列 vs Quorum Queues
      • 152)消息可靠性:Publisher Confirm 原理
      • 153)消息可靠性:Return Listener
      • 154)消息积压:消费者扩容与预取
      • 155)死信队列(DLX)循环引用
      • 156)延迟队列实现对比
      • 157)消息顺序性:Sharding
      • 158)RabbitMQ 内存控制(Memory High Watermark)
      • 159)Lazy Queue(惰性队列)
      • 160)RabbitMQ 集群:普通集群 vs 镜像集群
      • 161)脑裂(Network Partition)处理
      • 162)消息幂等:全局 ID 生成
      • 163)优先级队列(Priority Queue)
      • 164)RPC 模式(Request-Reply)
      • 165)Federation 与 Shovel
      • 166)MassTransit / CAP 框架
      • 167)Exchange to Exchange Binding
      • 168)Connection vs Channel
      • 169)Prefetch Count 调优
      • 170)消息体大小限制
      • 171)RabbitMQ 监控指标
      • 172)消费者限流(Rate Limiting)
      • 173)RabbitMQ 插件机制
      • 174)临时队列(Temporary Queue)
      • 175)Trace 日志(Firehose)
      • 176)消息版本控制
      • 177)事务消息(TX)
      • 178)vhost(虚拟主机)
      • 179)Erlang Cookie
      • 180)RabbitMQ 运维:节点下线
  • 进阶
  • 高级
  • 架构师
  • 《技术题》
moklgy
2026-02-06
目录

中级

# 🟡 中级梯队:C# 高级特性与内存管理(1–30)

# 1)async/await 状态机详解

面试官: async 方法编译后变成了什么?状态机是怎么工作的?
候选人(要点): 编译器生成一个实现 IAsyncStateMachine 的结构体,把方法拆成多个状态节点;await 点保存上下文(局部变量、执行位置)并注册 continuation;IO 完成后由线程池线程调用 MoveNext() 恢复执行。[3][4]
面试官追问: 如果 await 的 Task 已经是完成状态(同步完成),还会发生线程切换吗?这种情况下 ValueTask 有什么优势?

# 2)ConfigureAwait(false) 深度

面试官: ConfigureAwait(false) 到底在做什么?ASP.NET Core 里还需要写它吗?
候选人(要点): 它告诉状态机不要强制恢复到原来的同步上下文;在 Core 里默认没有 SynchronizationContext,所以通常不强制要求,但在库代码里写上更安全(避免在某些宿主环境下死锁)。
面试官追问: WinForms/WPF 里不写会怎样?为什么 UI 线程会死锁?你能画出死锁的调用链吗?

# 3)Task.Run vs Task.Factory.StartNew

面试官: Task.Run 和 Task.Factory.StartNew 区别?什么时候必须用后者?
候选人(要点): Task.Run 是简化 API,固定用线程池并默认 DenyChildAttach;StartNew 可指定调度器、支持父子任务附加等高级场景。通常推荐 Task.Run,除非需要精细控制任务层级或自定义 TaskScheduler。
面试官追问: 父子任务附加(AttachedToParent)有什么用?不显式指定会有什么坑?

# 4)CancellationToken 最佳实践

面试官: CancellationToken 怎么用?不传会怎样?
候选人(要点): Token 用于优雅取消异步操作;不传会导致任务无法及时停止、资源浪费、超时堆积。
面试官追问: 如果你的 async 方法里调了第三方库且它不接受 Token,你怎么做超时控制?Task.WhenAny + Task.Delay 的坑是什么?

# 5)SynchronizationContext 深度

面试官: 什么是 SynchronizationContext?为什么 ASP.NET Core 去掉了它?
候选人(要点): 它是"上下文切换抽象",让 await 后能回到特定上下文(UI 线程/ASP.NET 请求上下文)。Core 去掉是为了性能:避免不必要的线程切换与队列调度,任何线程池线程都可以继续执行。[5]
面试官追问: 这对异常传播有什么影响?为什么 Core 里 async void 异常处理更危险?

# 6)GC 深度:分代与提升

面试官: 什么情况下对象会从 Gen 0 提升到 Gen 1?Gen 2 回收为什么慢?
候选人(要点): Gen 0 GC 后存活的对象提升到 Gen 1;Gen 2 扫描范围大且可能触发 Full GC(包括 LOH),导致 STW 时间长。[6]
面试官追问: 大对象(85KB+)为什么直接进 LOH?LOH 的碎片问题怎么缓解?

# 7)Span 与 Memory 深度

面试官: Span<T> 为什么不能跨 await?Memory<T> 解决了什么问题?
候选人(要点): Span<T> 是 ref struct,只能在栈上(避免逃逸到堆),编译器禁止跨异步边界;Memory<T> 是普通 struct 但包含对象引用,可以跨 await,适合异步 IO 场景。
面试官追问: 什么场景下你必须用 Span?如果在高频字符串解析里怎么利用它避免 Substring 分配?

# 8)ArrayPool 实战

面试官: ArrayPool<T> 什么时候用?租还时有什么坑?
候选人(要点): 用于复用大数组(避免 LOH 压力),特别是临时缓冲区;租还必须成对且不能在归还后继续使用,否则会出现"数据被别人覆盖"的诡异 bug。
面试官追问: 租到的数组内容是干净的吗?什么时候需要手动清零?

# 9)weak reference(弱引用)

面试官: 什么是弱引用?你在什么场景用过?
候选人(要点): 弱引用允许 GC 在需要时回收对象,即使引用还在;适合缓存、订阅清理等"可失去"的场景。
面试官追问: 弱引用能防内存泄漏吗?事件订阅为什么还是要显式取消订阅?

# 10)finalizer 与 GC 压力

面试官: Finalizer(析构函数)会让对象经历几次 GC?为什么说它增加 GC 压力?
候选人(要点): 有 Finalizer 的对象至少两次:第一次进入 finalization queue,Finalizer 线程执行后第二次才真正回收。Finalizer 还会拖慢提升速度、增加 Gen 2 扫描成本。
面试官追问: SafeHandle 为什么比手写 Finalizer 更好?它的实现原理是什么?

# 11)record 类型深度

面试官: C# 9 的 record 本质是什么?它和 class / struct 有什么区别?
候选人(要点): record 是引用类型但具有值语义(基于内容的相等性、with 表达式生成副本),适合不可变数据传输对象(DTO)。
面试官追问: record struct 和普通 struct 区别?什么时候 record 不适合(如大对象频繁拷贝)?

# 12)Primary Constructor(主构造函数)

面试官: C# 12 的主构造函数有什么用?它和传统构造函数的区别?
候选人(要点): 减少样板代码,参数作为隐式字段捕获;适合简单依赖注入场景。
面试官追问: 主构造函数的参数作用域是整个类,这会不会导致闭包陷阱(比如在方法里访问已经过期的参数)?

# 13)Nullable Reference Types 深度

面试官: #nullable enable 做了什么?它能真正防止 NullReferenceException 吗?
候选人(要点): 编译期静态分析,给出警告/错误;运行时仍可能空引用(因为反序列化、反射、外部库等绕过检查)。
面试官追问: ! 强制非空操作符滥用会怎样?怎么在旧代码库渐进式启用 nullable?

# 14)Pattern Matching 深度

面试官: 模式匹配(Pattern Matching)有什么实际价值?给一个替代 if-else 的例子。
候选人(要点): 简化类型判断与属性解构,提升可读性;例如 obj switch { Student s when s.Age > 18 => ..., Teacher => ... }。
面试官追问: 编译器如何优化 switch 表达式?它和传统 switch 在 IL 层面有什么不同?

# 15)Expression Tree(表达式树)实战

面试官: 表达式树在 EF Core 里的作用?为什么它能翻译成 SQL?
候选人(要点): 表达式树把代码表示为数据结构(AST),Provider 遍历并生成 SQL;这是 LINQ to SQL/EF 的基石。
面试官追问: 你能手写一个简单的表达式树并编译成委托吗?Visitor 模式怎么遍历表达式树?

# 16)Reflection 性能坑

面试官: 反射为什么慢?你会怎么优化频繁反射调用?
候选人(要点): 反射慢在元数据查找、权限检查、装箱拆箱、动态调用;优化手段包括缓存 MethodInfo、编译表达式树为委托、使用 DynamicMethod / Emit 生成代码。
面试官追问: Activator.CreateInstance vs Expression.New 编译的委托,性能差多少?

# 17)IDisposable 模式完整版

面试官: 标准的 Dispose 模式是怎样的?为什么要写 Dispose(bool disposing)?
候选人(要点): 支持子类继承重写、区分"用户显式调用"与"Finalizer 调用";disposing=true 时可释放托管资源,false 时只释放非托管。
面试官追问: IAsyncDisposable 什么时候用?异步 Dispose 里抛异常会怎样?

# 18)Interlocked 与原子操作

面试官: Interlocked 类是干什么的?为什么它比 lock 快?
候选人(要点): 提供原子操作(CAS/Compare-And-Swap),无需进入内核态或获取锁,适合简单的计数/标志位。
面试官追问: Interlocked.Increment 能保证可见性吗?volatile 和 Interlocked 的关系?

# 19)volatile 关键字深度

面试官: volatile 到底做了什么?它能防止指令重排吗?
候选人(要点): volatile 保证读写的内存屏障,防止编译器/CPU 重排序并保证可见性;但它不保证原子性(复合操作仍需锁/Interlocked)。
面试官追问: 双重检查锁定(DCL)为什么需要 volatile?不加会出现什么诡异 bug?

# 20)ThreadLocal 场景

面试官: ThreadLocal<T> 是做什么的?你在什么场景用它?
候选人(要点): 每个线程独立存储一份副本,避免共享状态竞争;常用于日志上下文、随机数生成器、非线程安全对象池化。
面试官追问: 异步代码里用 ThreadLocal 有什么坑(线程池线程复用导致上下文混乱)?AsyncLocal<T> 解决了什么?

# 21)线程池调优

面试官: 线程池的最小/最大线程数怎么调?什么情况下需要手动调?
候选人(要点): 默认最小值通常等于 CPU 核数,最大值很大;如果有大量同步阻塞操作(如同步 IO),可能需要提高最小线程数避免饥饿;异步场景通常不需要调。
面试官追问: 线程池饥饿(Thread Pool Starvation)是什么?怎么从监控指标里发现?

# 22)Parallel.ForEach 深度

面试官: Parallel.ForEach 什么时候比普通 foreach 快?什么时候反而更慢?
候选人(要点): CPU 密集、可并行、无共享状态时快;如果单次迭代极快或有锁竞争,调度开销反而拖累性能。
面试官追问: 怎么控制并行度(MaxDegreeOfParallelism)?并行里抛异常会怎样?

# 23)Channels (System.Threading.Channels)

面试官: Channel<T> 是做什么的?为什么比 BlockingCollection 更好?
候选人(要点): 高性能异步生产者-消费者队列,支持背压、限流;比 BlockingCollection 更轻量且对异步友好。
面试官追问: Bounded vs Unbounded Channel?生产者比消费者快时怎么处理背压?

# 24)Task vs Thread

面试官: Task 和 Thread 本质区别?为什么不推荐直接 new Thread?
候选人(要点): Task 是轻量异步工作单元,基于线程池调度;Thread 是重量级 OS 线程,创建销毁成本高且难管理。
面试官追问: 什么情况下你还是需要创建专用线程(如长时间运行、需要设置 ApartmentState)?

# 25)SemaphoreSlim 与限流

面试官: SemaphoreSlim 是干什么的?你怎么用它做并发限流?
候选人(要点): 控制同时访问资源的线程数(如限制并发数据库连接);通过 WaitAsync 实现异步等待。
面试官追问: 忘记 Release() 会怎样?怎么保证异常时也能释放信号量?

# 26)lock vs Monitor vs Mutex

面试官: lock、Monitor、Mutex 的区别?跨进程锁用哪个?
候选人(要点): lock 是 Monitor 的语法糖(进程内);Mutex 支持跨进程但性能更差。
面试官追问: Monitor.TryEnter 超时机制?死锁检测工具怎么用?

# 27)Lazy 线程安全模式

面试官: Lazy<T> 的线程安全模式有哪几种?默认是哪个?
候选人(要点): ExecutionAndPublication(默认,最安全)、PublicationOnly、None;选择取决于初始化成本与并发需求。
面试官追问: 如果初始化抛异常,Lazy 会缓存异常吗?后续访问会怎样?

# 28)ImmutableCollections 价值

面试官: 不可变集合(ImmutableList/Dictionary)有什么用?性能不是更差吗?
候选人(要点): 适合多线程共享、版本控制、函数式编程;虽然写入慢(拷贝开销),但读并发安全且无锁。
面试官追问: 内部实现是什么(持久化数据结构/平衡树)?什么时候不该用?

# 29)StringPool / Intern

面试官: 字符串驻留池(Intern Pool)是什么?String.Intern 有什么坑?
候选人(要点): 池化相同字面量字符串避免重复分配;但 Intern 会让字符串进入 Gen 2 且不释放,滥用会内存泄漏。
面试官追问: 什么场景适合 Intern(如固定枚举字符串)?什么场景禁止(用户输入)?

# 30)Encoding 与字符集

面试官: UTF-8 / UTF-16 / GBK 的区别?.NET 内部字符串用什么编码?
候选人(要点): .NET string 内部是 UTF-16;跨系统通信常用 UTF-8(更紧凑);转换不当会乱码或丢数据。
面试官追问: BOM(字节顺序标记)是什么?什么时候需要、什么时候会坑?

# 🟡 中级梯队:ASP.NET Core 高级特性与性能调优(31–60)[1]

# 31)中间件短路与条件分支

面试官: 中间件里不调用 next() 会怎样?给一个"缓存中间件直接返回结果"的例子。
候选人(要点): 短路管道,后续中间件与端点不会执行;缓存命中时可直接 await context.Response.WriteAsync() 并返回,节省后续计算。[2]
面试官追问: 如果在短路前已经有中间件修改了 Response Header/Body,会有什么风险?怎么保证幂等性?

# 32)IApplicationBuilder 管道构建原理

面试官: IApplicationBuilder 是怎么把中间件"串"起来的?用委托链还是其他?
候选人(要点): 通过委托链(RequestDelegate),每个中间件持有 next,调用 next 就是调用下一个委托;最终形成一个嵌套的 Func 链。[3]
面试官追问: 如果某个中间件构造耗时很长,会阻塞启动吗?怎么做异步初始化?

# 33)Endpoint Routing 深度

面试官: UseRouting 和 UseEndpoints 为什么要分开?中间放鉴权的意义?
候选人(要点): UseRouting 只做路由匹配与选择端点,UseEndpoints 执行端点;分离后可以在中间插入认证/授权中间件,基于路由元数据做决策。
面试官追问: 如果 UseAuthentication 放在 UseRouting 之前会怎样?路由匹配能拿到认证信息吗?

# 34)Filter 执行顺序与作用域

面试官: Authorization / Resource / Action / Exception / Result Filter 的执行顺序?哪个能短路?
候选人(要点): Authorization 最早(可短路),Resource 可做缓存(可短路),Action 在执行前后,Exception 捕获异常,Result 在返回前后。
面试官追问: 如果在 Action Filter 里修改了 ActionExecutingContext.Result,后续会怎样?

# 35)全局异常处理:Middleware vs Filter

面试官: 全局异常用中间件还是过滤器?各自优缺点?
候选人(要点): 中间件能捕获整个管道(包括静态文件、路由错误),过滤器只在 MVC 管道内;中间件更通用,过滤器能访问 MVC 上下文(如 ModelState)。[4]
面试官追问: 如果异常发生在 Response 已经开始写入后(如流式传输),怎么处理?

# 36)模型绑定深度

面试官: 模型绑定的优先级(Body/Query/Route/Form)?如何自定义 Model Binder?
候选人(要点): [ApiController] 会自动推断(复杂类型走 Body,简单类型走 Query/Route);自定义 Binder 实现 IModelBinder 并注册到 ModelBinderProviders。
面试官追问: 绑定失败但没有验证错误,怎么调试?BindingSource 元数据有什么用?

# 37)模型验证与 FluentValidation

面试官: DataAnnotations 验证的局限性?为什么很多项目用 FluentValidation?
候选人(要点): DataAnnotations 耦合在实体上、表达能力有限、难测试;FluentValidation 分离验证逻辑、支持复杂规则与依赖注入、更易测试。
面试官追问: FluentValidation 的异步验证规则(如查库验证唯一性)有什么性能风险?

# 38)IOptions 模式深度

面试官: IOptions<T>、IOptionsSnapshot<T>、IOptionsMonitor<T> 分别适合什么生命周期的服务?
候选人(要点): IOptions 适合 Singleton(只读一次),Snapshot 适合 Scoped(每次请求刷新),Monitor 适合需要变更通知的场景(如配置热更新)。
面试官追问: IOptionsMonitor.OnChange 回调在什么线程执行?并发安全吗?

# 39)配置源优先级与覆盖

面试官: appsettings.json / 环境变量 / 命令行参数的优先级?怎么做敏感配置管理?
候选人(要点): 后注册的源优先级更高(命令行 > 环境变量 > json);敏感配置用 Azure Key Vault / AWS Secrets Manager / User Secrets(开发)。
面试官追问: 配置变更后怎么让应用无缝重载(不重启)?哪些组件不支持热更新?

# 40)日志结构化与 Serilog

面试官: 为什么推荐结构化日志?{UserId} 和 $"{UserId}" 的区别?
候选人(要点): 结构化日志可以按字段查询、聚合、告警;占位符 {UserId} 会被解析为属性,字符串插值会丢失语义。
面试官追问: 日志量大时怎么优化性能(异步写入、采样、日志级别动态控制)?

# 41)Kestrel 性能调优

面试官: Kestrel 的连接限制、请求超时、HTTP/2 配置在哪里设置?
候选人(要点): 在 ConfigureKestrel 中设置 Limits(如 MaxConcurrentConnections、KeepAliveTimeout);HTTP/2 默认启用,注意客户端兼容性。
面试官追问: 什么情况下需要调大 MaxRequestBodySize?调大有什么风险(DoS)?

# 42)响应压缩

面试官: 响应压缩中间件(UseResponseCompression)默认压什么?动态内容会压吗?
候选人(要点): 默认压 text/* 和 application/json 等 MIME 类型;动态内容会压,但要注意 CPU 成本与延迟。
面试官追问: HTTPS 下压缩为什么有安全风险(CRIME/BREACH 攻击)?怎么缓解?

# 43)CORS 深度

面试官: CORS 预检请求(OPTIONS)什么时候触发?怎么优化预检成本?
候选人(要点): 非简单请求(如带自定义 Header、PUT/DELETE 方法)会触发预检;可通过 Access-Control-Max-Age 让浏览器缓存预检结果。
面试官追问: WithOrigins("*") 和 AllowAnyOrigin() 有什么区别?为什么通配符不能和 AllowCredentials 共存?

# 44)健康检查(Health Checks)

面试官: 健康检查怎么做?你会检查哪些依赖(DB/Redis/MQ)?
候选人(要点): 使用 AddHealthChecks 注册检查项,暴露端点(如 /health)供负载均衡/监控探测;检查关键依赖可用性。
面试官追问: 健康检查失败但服务仍在运行,会发生什么?K8s 的 Readiness vs Liveness 探针区别?

# 45)静态文件与 CDN

面试官: UseStaticFiles 做了什么?为什么生产环境要用 CDN?
候选人(要点): 中间件直接从 wwwroot 返回文件,短路后续管道;CDN 减少应用服务器带宽压力、提升全球访问速度、缓存边缘节点。
面试官追问: 静态文件版本控制(缓存失效)怎么做?asp-append-version 的原理?

# 46)SignalR 连接管理

面试官: SignalR 的传输协议(WebSocket/SSE/Long Polling)是怎么协商的?
候选人(要点): 客户端按优先级尝试,服务端根据支持情况选择最优;WebSocket 双向、低延迟,SSE 单向、简单,Long Polling 兜底。
面试官追问: 横向扩展多实例时,SignalR 的消息怎么广播?Redis Backplane 原理?

# 47)授权策略(Policy-Based Authorization)

面试官: 基于角色的授权 vs 基于策略的授权?给一个"只有华东区经理能审核"的策略。
候选人(要点): 角色授权简单但不灵活;策略授权可组合多种要求(角色+声明+自定义逻辑),实现细粒度控制。
面试官追问: 如何在策略里访问数据库(依赖注入 DbContext)?异步授权处理器怎么写?

# 48)JWT 刷新令牌(Refresh Token)

面试官: 为什么需要 Refresh Token?它和 Access Token 的生命周期设计?
候选人(要点): Access Token 短命(15分钟)减少泄露风险;Refresh Token 长命(天/周)存储在安全位置(HttpOnly Cookie),用于换取新 Access Token。
面试官追问: Refresh Token 怎么做失效管理?被盗用怎么检测(Token Rotation/Family)?

# 49)Rate Limiting(限流)

面试官: ASP.NET Core 7+ 内置的限流中间件怎么用?固定窗口 vs 滑动窗口?
候选人(要点): 使用 AddRateLimiter 注册策略;固定窗口简单但有突刺问题,滑动窗口更平滑但复杂。
面试官追问: 分布式限流怎么做(Redis 计数器/令牌桶)?怎么区分"按用户"还是"按IP"?

# 50)输出缓存(Output Caching)

面试官: 输出缓存和响应缓存的区别?什么场景适合用输出缓存?
候选人(要点): 输出缓存在服务端缓存完整响应(含 Header/Body),响应缓存靠 HTTP 缓存头让客户端/CDN 缓存;输出缓存适合昂贵计算的只读接口。
面试官追问: 缓存 Key 怎么设计(按查询参数/用户/租户)?怎么做缓存失效(Tag-Based Invalidation)?

# 51)后台任务(Hosted Service)

面试官: IHostedService 和 BackgroundService 的区别?怎么做定时任务?
候选人(要点): BackgroundService 是 IHostedService 的抽象基类,简化长时间运行任务;定时任务可用 PeriodicTimer 或第三方库(Hangfire/Quartz)。
面试官追问: Hosted Service 里怎么正确使用 Scoped 服务(DbContext)?启动失败会影响应用启动吗?

# 52)gRPC 深度

面试官: gRPC 相比 REST 的优势?什么场景适合用 gRPC?
候选人(要点): 二进制协议(Protobuf)更紧凑、强类型契约、双向流、性能更高;适合微服务内部通信、高吞吐低延迟场景。
面试官追问: gRPC 在浏览器里能直接用吗?gRPC-Web 是什么?

# 53)Minimal API vs Controller

面试官: Minimal API 的优缺点?什么项目适合用它?
候选人(要点): 优点是简洁、启动快、适合小项目/微服务;缺点是缺少 Controller 的约定(如模型绑定、过滤器)、可测试性稍差。
面试官追问: Minimal API 里怎么做复杂的参数验证和全局错误处理?

# 54)HttpClient 工厂模式

面试官: 为什么不能 new HttpClient?IHttpClientFactory 解决了什么问题?
候选人(要点): new HttpClient 会导致 Socket 耗尽和 DNS 不刷新;工厂模式复用 Handler 并管理连接池生命周期。
面试官追问: Named Client vs Typed Client?怎么配置重试策略(Polly)?

# 55)请求/响应压缩与流式传输

面试官: 大文件上传/下载怎么做流式传输?为什么不能全读到内存?
候选人(要点): 使用 Stream 分块读写,配合 MultipartReader(上传)或 FileStreamResult(下载);全读内存会 OOM。
面试官追问: 流式传输时怎么做进度报告?取消令牌怎么用?

# 56)API 版本控制

面试官: API 版本控制有哪几种策略(URL/Header/Query)?你推荐哪种?
候选人(要点): URL 版本(/api/v1/users)最直观;Header 版本(Api-Version)更 RESTful;Query String 灵活但容易被忽略。
面试官追问: 怎么做版本共存与向后兼容?怎么处理大版本升级的数据迁移?

# 57)请求日志与 TraceId

面试官: 怎么在日志里关联同一个请求的所有日志?分布式追踪怎么做?
候选人(要点): 使用 HttpContext.TraceIdentifier 或 Activity.TraceId;分布式追踪靠传播 TraceId(如 W3C Trace Context Header)。
面试官追问: OpenTelemetry 是什么?怎么集成到 ASP.NET Core?

# 58)内存缓存(IMemoryCache)深度

面试官: IMemoryCache 怎么防止内存泄漏?绝对过期 vs 滑动过期?
候选人(要点): 设置 Size 和 SizeLimit 限制总内存;绝对过期是固定时间,滑动过期是"最后访问后 X 时间"(适合会话/热数据)。
面试官追问: 并发访问同一个 Key 时,GetOrCreate 会重复计算吗?怎么保证只计算一次?

# 59)分布式缓存(IDistributedCache)

面试官: IDistributedCache 抽象的好处?Redis vs SQL Server 分布式缓存?
候选人(要点): 抽象让实现可替换(开发用内存、生产用 Redis);Redis 性能更好,SQL Server 适合已有基础设施且不想引入新组件的场景。
面试官追问: 分布式缓存的一致性怎么保证?多实例同时写会怎样?

# 60)WebSockets 生命周期管理

面试官: WebSocket 连接怎么管理?客户端断线怎么检测?
候选人(要点): 使用 HttpContext.WebSockets.AcceptWebSocketAsync 升级连接;通过心跳检测或读取超时判断断线。
面试官追问: 长连接在负载均衡后怎么保持会话亲和性(Sticky Session)?水平扩展时怎么做消息广播?


# 🟡 中级梯队:EF Core 性能、数据库锁与高级查询(61–90)[1]

# 61)N+1 进阶与 SplitQuery

面试官: SplitQuery 到底是为了解决什么问题?它和普通 Include 的性能权衡点在哪?
候选人(要点): 普通 Include 产生笛卡尔积,数据冗余大;SplitQuery 拆成多条 SQL 避免冗余,但增加了数据库往返次数(RTT)。在"主表少、子表多"或"子表包含大字段"时,SplitQuery 性能更好;反之普通 Include 更好。[2]
面试官追问: 如果 SplitQuery 执行了一半(比如第一条 SQL 成功,第二条失败),数据一致性怎么保证?

# 62)EF Core 批量操作(Bulk Update/Delete)

面试官: EF Core 7 引入的 ExecuteUpdate/ExecuteDelete 为什么比传统方式快?
候选人(要点): 传统方式需要先查询实体到内存、修改状态、再 SaveChanges(多条 SQL);新 API 直接生成 UPDATE/DELETE WHERE 语句,零内存加载、单条 SQL 执行。
面试官追问: 这种批量操作会触发实体的 ChangeTracker 和本地拦截器吗?为什么?

# 63)乐观锁与并发令牌

面试官: RowVersion 这种乐观锁机制,除了 update,在 delete 时也能生效吗?为什么?
候选人(要点): 能生效。DELETE 语句也可以带 WHERE Id = x AND RowVersion = v;如果行版本变了或行没了,受影响行数为 0,抛出并发异常。[3][4]
面试官追问: 并发冲突发生后,你的重试策略是怎样的?无限重试会把数据库打挂吗?

# 64)悲观锁与事务隔离级别

面试官: 什么时候必须用悲观锁(XLOCK / FOR UPDATE)?EF Core 怎么加悲观锁?
候选人(要点): 必须强一致且竞争激烈的场景(如秒杀扣库存);EF Core 原生 API 不支持 Hint,需用 FromSqlRaw("SELECT ... WITH (UPDLOCK) ...") 或 BeginTransaction(IsolationLevel.Serializable)。
面试官追问: 高并发下悲观锁会导致死锁吗?怎么避免?

# 65)连接池耗尽(Connection Pool Exhaustion)

面试官: 生产环境报"连接池超时",可能的原因有哪些?除了并发大,还有什么?
候选人(要点): 连接泄漏(未 Dispose)、慢查询(占用连接时间长)、事务未提交/回滚、连接池设置过小。最常见是代码里手动 new SqlConnection 没关。[5]
面试官追问: 怎么用监控指标(如 ADO.NET Performance Counters)定位是泄漏还是并发不够?

# 66)SQL 注入进阶:动态排序

面试官: 参数化查询能防 WHERE 注入,那 ORDER BY 动态列名怎么防?OrderBy(input) 安全吗?
候选人(要点): 参数化不支持列名(只能是值);OrderBy(input) 如果 input 是用户拼接的 SQL 片段会注入。防御:使用白名单映射(Dictionary<string, string>),或使用強类型 Lambda 表达式构建器。
面试官追问: EF Core 的 FromSqlRaw 如果传入的是 $"{user_input}" 会发生什么?它是参数化还是拼接?

# 67)死锁(Deadlock)分析

面试官: SQL Server 报"死锁",你怎么读死锁图(Deadlock Graph)?
候选人(要点): 看资源(Resource)和所有者(Owner)/请求者(Waiter);A 拿了 X 等 Y,B 拿了 Y 等 X;通常是加锁顺序不一致或索引缺失导致的扫表锁升级。
面试官追问: 怎么用 NOLOCK 或 Snapshot Isolation 缓解死锁?副作用是什么?

# 68)索引失效深度

面试官: LIKE '%abc' 为什么不走索引?有什么技术能优化这种左模糊查询?
候选人(要点): B+ 树是按前缀排序的,左模糊无法利用有序性(只能全扫);优化可用全文索引(Full-Text Search)、倒排索引(ES)或计算列反转字符串索引。
面试官追问: OR 条件两边都有索引,一定会走索引吗?为什么有时候优化器选择全表扫?

# 69)执行计划(Execution Plan)

面试官: 怎么看执行计划里的"Key Lookup"(书签查找)?为什么它是性能杀手?
候选人(要点): Key Lookup 意味着非聚集索引没覆盖所有列,需要回聚集索引取数据(随机 IO);大量回表导致性能急剧下降。优化方案是使用覆盖索引(Include 列)。
面试官追问: "Index Scan" 和 "Index Seek" 的区别?Scan 一定比 Seek 慢吗?

# 70)复杂查询:Group By 优化

面试官: 大数据量 GROUP BY 慢怎么优化?
候选人(要点): 确保 Group By 字段有索引(消除排序开销);减少聚合数据量(WHERE 先过滤);预计算(物化视图/中间表)。
面试官追问: COUNT(DISTINCT col) 在大数据下为什么极慢?有什么近似算法替代(HyperLogLog)?

# 71)临时表 vs 表变量 vs CTE

面试官: 存储过程里用临时表(#Temp)还是表变量(@Table)?CTE(公用表表达式)是临时的吗?
候选人(要点): 临时表支持索引/统计信息,适合大数据量;表变量在内存(也可能落盘),无统计信息,适合小数据。CTE 是语法糖(视图),不存储数据(除非递归 CTE 可能溢出)。
面试官追问: 递归 CTE 怎么写?如果不加最大递归深度限制会怎样?

# 72)TVP(表值参数)

面试官: 怎么一次性插入 1 万条明细数据?循环 Insert 还是别的?
候选人(要点): 循环 Insert 最慢(网络开销大);可用 SqlBulkCopy(最快)或 TVP(表值参数)传给存储过程;EF Core 的 AddRange + SaveChanges 也会自动批处理(Batching)。
面试官追问: SqlBulkCopy 的事务怎么控制?

# 73)分库分表策略

面试官: 垂直拆分和水平拆分的区别?水平拆分(Sharding)后的分页查询怎么做?
候选人(要点): 垂直按业务模块拆(列/表),水平按数据量拆(行)。水平分表后的全局分页最难:只能"全局查再排序截取"(性能差)或"禁止跳页"(只提供下一页,按 ID 游标)。
面试官追问: 分库后的分布式事务(跨库转账)怎么解?(TCC / 消息最终一致性)。

# 74)数据库游标(Cursor)

面试官: 为什么 DBA 都不让用游标?集合操作 vs 逐行操作?
候选人(要点): 游标是逐行处理,无法利用集合算法优化,持有锁时间长,性能极差。应尽量用 Set-based SQL(UPDATE ... FROM ...)。
面试官追问: 必须逐行处理的场景(如复杂的跨行状态依赖计算)怎么优化?(Window Function / 递归 CTE)。

# 75)Window Function(窗口函数)

面试官: ROW_NUMBER()、RANK()、DENSE_RANK() 区别?给一个"分组取最新一条"的 SQL。
候选人(要点): ROW_NUMBER 连续唯一(1,2,3),RANK 并列跳号(1,1,3),DENSE_RANK 并列不跳号(1,1,2)。分组取最新:PARTITION BY GroupId ORDER BY Time DESC 取 RowNum=1。
面试官追问: 窗口函数是在 WHERE 之前还是之后执行?能放在 WHERE 里吗?

# 76)JSON 数据存储

面试官: SQL Server / MySQL 的 JSON 类型好用吗?和 MongoDB 怎么选?
候选人(要点): 关系型数据库存 JSON 适合"大部分结构化 + 少量扩展字段";如果全是 JSON 且读写高频、Schema 极不稳定,MongoDB 更优。
面试官追问: 在 JSON 字段上怎么建索引?性能如何?

# 77)CDC(Change Data Capture)

面试官: 怎么实时同步数据库变更到 ES/Redis?触发器行吗?
候选人(要点): 触发器性能差且耦合。CDC(如 Debezium / Canal)读取数据库事务日志(Binlog / LSN),异步推送到 MQ,性能影响小且可靠。
面试官追问: 软删除的数据 CDC 能捕获到吗?全量同步 + 增量同步怎么配合?

# 78)数据库备份与恢复模型

面试官: 完整恢复模式(Full Recovery)和简单模式(Simple)的区别?事务日志为什么会撑爆磁盘?
候选人(要点): 完整模式记录所有日志,支持点对点恢复(PITR),日志需要手动备份截断,否则无限增长;简单模式自动截断日志,只能恢复到全备点。
面试官追问: "尾日志备份"(Tail-Log Backup)是什么?灾难恢复时的最后一步?

# 79)EF Core 拦截器(Interceptor)

面试官: 怎么在 SQL 执行前后做埋点(如慢 SQL 日志、动态改表名)?
候选人(要点): 继承 DbCommandInterceptor,重写 ReaderExecuting 等方法。可修改 CommandText 实现分表逻辑,或记录耗时。
面试官追问: 在拦截器里能修改 Result 吗?怎么实现二级缓存(查询拦截)?

# 80)Compiled Query(编译查询)

面试官: EF.CompileQuery 是什么?为什么能提升性能?
候选人(要点): 预编译 LINQ 表达式树为委托,避免每次查询都重新解析表达式树的开销;适合高频执行的简单查询。
面试官追问: 编译查询支持异步吗?有什么限制?

# 81)Shadow Property 实战

面试官: 怎么用影子属性实现通用的"最后修改人/时间"记录?
候选人(要点): 在 OnModelCreating 里统一为所有实体注册 Shadow Property;在 SaveChanges 拦截器里自动赋值。
面试官追问: 查询时怎么拿到影子属性的值?(EF.Property<T>(blog, "Created"))。

# 82)Value Conversion(值转换)

面试官: 怎么把数据库里的 int 映射成代码里的 Enum?或者把 JSON 字符串映射成对象?
候选人(要点): 使用 HasConversion;定义转换器(Provider <-> Model)。适合处理枚举、加密字段、JSON 对象。
面试官追问: 值转换后的字段能做 LINQ 查询条件吗?翻译成的 SQL 效率如何?

# 83)Owned Entity Type(从属实体)

面试官: OwnsOne 是什么?和普通导航属性区别?
候选人(要点): 从属实体没有独立 ID,数据通常内嵌在主表(多列)或共享主键;适合值对象(Value Object),如 Address (City, Street)。
面试官追问: OwnsMany(集合)怎么存储?(通常是独立表)。

# 84)Global Query Filter 进阶

面试官: 全局过滤器(软删除)会影响 IgnoreQueryFilters() 后的性能吗?
候选人(要点): 过滤器本质是追加 WHERE 条件;加上索引(如 IsDeleted)则不影响,没索引可能全表扫。IgnoreQueryFilters 移除 WHERE,可能利用不同的索引。
面试官追问: 多租户过滤器在关联查询(Include)时生效吗?(生效,自动追加到 JOIN 条件)。

# 85)DbContext Pooling

面试官: AddDbContextPool 为什么能提升性能?有什么坑?
候选人(要点): 复用 Context 实例,减少创建/销毁开销;坑在于 Context 必须是无状态的(除了 DbConnection),如果注入了有状态的 Scoped 服务且没重置,会发生状态污染。
面试官追问: 怎么正确重置 Context 状态?

# 86)EF Core 8 Primitive Collections

面试官: EF Core 8 支持 List<int> 直接存库了吗?底层是怎么存的?
候选人(要点): 支持;底层通常存为 JSON 数组(SQL Server / PG)或特定分隔字符串,查询时支持 Contains 翻译。
面试官追问: 这种 JSON 数组查询性能如何?能走索引吗?

# 87)HierarchyId 与树形结构

面试官: 怎么存储树形结构(如部门树)?递归查询怎么优化?
候选人(要点): 邻接表(ParentId)查询慢(递归);SQL Server HierarchyId 类型存储路径,支持高效的子树查询/层级判断;或用闭包表/路径枚举法。
面试官追问: 删除中间节点时,子节点怎么处理?

# 88)Spatial Data(空间数据)

面试官: 怎么存经纬度并查"附近 5 公里的人"?
候选人(要点): 使用 NetTopologySuite 库,数据库用 Geography 类型(SQL Server / PostGIS);利用空间索引做 ST_Distance 查询。
面试官追问: 简单的矩形范围查询 vs 真实的地理距离计算?

# 89)In-Memory Database 坑

面试官: 单元测试推荐用 EF Core In-Memory 吗?为什么有人反对?
候选人(要点): 不推荐。In-Memory 行为与真实 DB 差异大(不支持事务隔离、约束检查弱、LINQ 翻译不同);推荐用 Testcontainers 跑真实数据库容器。
面试官追问: SQLite In-Memory 模式怎么样?(比纯内存库好,但仍有 SQL 语法差异)。

# 90)Raw SQL 与 Dapper 混用

面试官: EF Core 项目里什么时候需要引入 Dapper?怎么共享事务?
候选人(要点): 复杂报表/极致性能查询用 Dapper;共享事务需从 DbContext.Database.GetDbConnection() 获取连接,并使用 DbContext.Database.CurrentTransaction.GetDbTransaction() 传递事务对象。
面试官追问: Dapper 的查询结果怎么做 Change Tracking?(不能,是只读的)。

# 🟡 中级梯队:设计模式进阶与架构模式(91–120)[1]

# 91)单例模式的线程安全与破坏

面试官: 饿汉式、懒汉式、双重检查锁(DCL)的区别?DCL 为什么要判两次空?
候选人(要点): 饿汉类加载即创建(安全但可能浪费);懒汉延迟加载(需加锁);DCL 第一次判空避锁(性能),第二次判空保单例(安全)。
面试官追问: 反射和序列化能破坏单例吗?怎么防止?(枚举单例 / readResolve)。

# 92)简单工厂 vs 工厂方法 vs 抽象工厂

面试官: 什么时候该升级到工厂方法?抽象工厂到底是解决什么问题的?
候选人(要点): 简单工厂违背 OCP(加新类改 switch);工厂方法符合 OCP(每类一工厂);抽象工厂解决"产品族"(如 Windows 按钮+文本框 vs Mac 按钮+文本框)的一致性创建。
面试官追问: .NET Core DI 容器里的 AddTransient<IFoo, Foo>() 本质是哪种工厂模式?

# 93)代理模式 vs 装饰器模式

面试官: 结构都是"持有一个对象并实现相同接口",区别在哪?
候选人(要点): 意图不同。代理(Proxy)控制访问(权限、延迟加载、远程调用);装饰器(Decorator)增强功能(日志、缓存、加密)。
面试官追问: 动态代理(Dynamic Proxy)是什么?AOP(如 Castle DynamicProxy)是怎么实现的?

# 94)适配器模式(Adapter)实战

面试官: 你的系统要接一个新的第三方支付,接口和现在的完全不一样,怎么用适配器?
候选人(要点): 定义统一目标接口 IPayment;创建 AliPayAdapter 实现该接口,内部持有 AliPaySdk 并转换调用逻辑。
面试官追问: 类适配器(继承)和对象适配器(组合)选哪个?(优先组合,支持多继承/灵活性)。

# 95)观察者模式 vs 发布订阅

面试官: 观察者模式和发布-订阅模式(Pub/Sub)是一回事吗?
候选人(要点): 不完全一样。观察者通常是直接耦合(Subject 持有 Observer 列表);Pub/Sub 通常有中间 Broker(如 EventBus/MQ),发布者不认识订阅者。
面试官追问: 在 C# 中实现弱引用观察者(Weak Event Pattern)是为了解决什么问题?

# 96)策略模式与工厂结合

面试官: 只有策略模式,客户端还是要 new StrategyA(),怎么消除这个依赖?
候选人(要点): 结合工厂模式或 DI。创建一个 StrategyFactory,根据上下文(如 UserType)返回对应的 IStrategy 实例;或利用 DI 的 IEnumerable<IStrategy> 注入并按 Key 查找。
面试官追问: 如果策略很多,怎么避免工厂里的 switch-case 爆炸?(使用字典映射 / 反射扫描)。

# 97)模板方法模式(Template Method)

面试官: ASP.NET Core 的生命周期里哪里体现了模板方法?
候选人(要点): Controller 基类的 OnActionExecuting、OnActionExecuted 等虚方法提供了钩子;框架定义了执行流程骨架,子类重写特定步骤。
面试官追问: 模板方法和回调函数(Callback)相比,优缺点是什么?

# 98)责任链模式(Chain of Responsibility)

面试官: 中间件管道是责任链,那业务代码里什么时候用?
候选人(要点): 复杂审批流(主管 -> 经理 -> 总监)、多级校验规则。链条上的每个节点决定是否处理或传给下家。
面试官追问: 纯责任链(只处理或传)和不纯责任链(处理一部分再传)的区别?

# 99)建造者模式(Builder)

面试官: StringBuilder 是建造者模式吗?Fluent API(如 EF Core 配置)呢?
候选人(要点): 是变体。传统 Builder 分离"构建过程"与"表示";Fluent API 侧重于链式调用构建复杂对象配置,提升可读性。
面试官追问: 什么时候用 Builder 而不是长参数构造函数?(参数多、组合灵活、需校验构建步骤)。

# 100)原型模式(Prototype)与深拷贝

面试官: ICloneable 为什么不推荐用?怎么实现深拷贝?
候选人(要点): Clone 语义模糊(深拷贝还是浅拷贝?)。深拷贝推荐序列化(JSON/Binary)或手动递归复制;浅拷贝用 MemberwiseClone。
面试官追问: 序列化做深拷贝的性能损耗怎么解决?(使用表达式树/Emit 生成拷贝代码)。

# 101)享元模式(Flyweight)

面试官: 字符串驻留池是享元模式吗?还有什么场景用享元?
候选人(要点): 是。场景:大量细粒度对象的复用(如围棋棋子、游戏里的树木模型),提取内部状态(共享)与外部状态(传入)。
面试官追问: 享元模式如何解决线程安全问题?(内部状态必须不可变)。

# 102)组合模式(Composite)

面试官: 怎么处理树形结构(如菜单、部门)的统一操作?
候选人(要点): 让"叶子节点"和"组合节点"实现相同接口(Component),组合节点持有 List<Component>;客户端可以一致对待单个对象和组合对象。
面试官追问: 透明性(接口有 Add/Remove)vs 安全性(只在组合节点有 Add/Remove)的权衡?

# 103)命令模式(Command)

面试官: 为什么要封装"命令"对象?直接调用方法不行吗?
候选人(要点): 解耦请求者与接收者;支持撤销/重做(Undo/Redo)、宏命令(组合)、队列化执行(如任务队列)。
面试官追问: CQRS 里的 Command 是命令模式吗?(是,承载意图的数据对象)。

# 104)状态模式(State)

面试官: 订单状态流转(待支付 -> 待发货 -> 已完成)怎么消除大量 if-else?
候选人(要点): 将每种状态封装成类,实现统一接口 Handle();Context 持有当前状态,委托给状态对象处理行为并切换状态。
面试官追问: 状态模式和策略模式结构很像,区别在哪?(状态模式内部控制状态流转,客户端不关心;策略模式由客户端指定策略)。

# 105)中介者模式(Mediator)

面试官: MediatR 库用过吗?它属于什么模式?解决了什么问题?
候选人(要点): 中介者模式。解决对象间网状依赖(紧耦合),改为星状依赖(只依赖中介者);实现进程内消息解耦(CQRS Handler)。
面试官追问: 中介者会不会变成"上帝对象"(God Object)?怎么避免?

# 106)迭代器模式(Iterator)与 yield

面试官: foreach 怎么工作的?yield return 编译成了什么?
候选人(要点): 依赖 IEnumerable / IEnumerator;yield 编译成状态机类,按需生成序列元素(延迟执行)。
面试官追问: 在 yield 方法里写 try-catch 和 try-finally 有什么限制?

# 107)备忘录模式(Memento)

面试官: 怎么实现"保存草稿"或"回滚配置"功能?
候选人(要点): 捕获对象内部状态并存到外部(Memento),且不破坏封装(只有原发器能读 Memento 内容)。
面试官追问: 状态量大时怎么优化内存?(增量存储 / 序列化存储)。

# 108)访问者模式(Visitor)

面试官: 表达式树遍历(ExpressionVisitor)是怎么实现的?
候选人(要点): 数据结构(AST节点)稳定,操作(打印、编译、分析)多变;利用双分派(Accept(Visitor) -> Visitor.Visit(this))在不改节点类的情况下扩展操作。
面试官追问: 为什么说访问者模式破坏了封装?(Visitor 需要访问节点内部细节)。

# 109)桥接模式(Bridge)

面试官: 什么时候用桥接而不是继承?
候选人(要点): 当一个类有多个维度的变化(如"形状"和"颜色"),继承会导致类爆炸(M*N);桥接通过组合将抽象部分与实现部分分离,独立变化。
面试官追问: JDBC / ADO.NET 驱动程序管理是桥接模式吗?

# 110)解释器模式(Interpreter)

面试官: 你做过简单的规则引擎吗?比如解析 "A > 10 AND B < 5"。
候选人(要点): 定义文法规则的类(AndExpression, GreaterExpression),构建语法树并解释执行。
面试官追问: 性能要求高时为什么不用解释器模式?(慢,推荐编译成 Expression Tree 或 IL)。

# 111)贫血模型 vs 充血模型(DDD 基础)

面试官: 为什么说只有 getter/setter 的实体是"贫血"的?充血模型长什么样?
候选人(要点): 贫血只有数据,逻辑散落在 Service,破坏封装;充血把业务逻辑(验证、状态变更)内聚在实体中,Service 只做编排。
面试官追问: EF Core 怎么支持充血模型?(私有字段映射、构造函数注入、只读属性)。

# 112)实体 vs 值对象(Value Object)

面试官: DDD 里实体和值对象的本质区别?值对象为什么是不可变的?
候选人(要点): 实体有唯一标识(ID),生命周期连续;值对象由属性定义身份(属性一样就是同一个),无 ID,不可变以保证副作用可控。
面试官追问: EF Core 怎么映射值对象?(OwnsOne / Complex Type)。

# 113)聚合根(Aggregate Root)设计

面试官: 聚合根的职责是什么?为什么不能直接修改聚合内部的实体?
候选人(要点): 维护一致性边界(事务边界)。外部只能通过聚合根操作内部,防止破坏业务规则(如"订单明细总额必须等于订单总额")。
面试官追问: 一个事务能修改多个聚合吗?(原则上不能,应通过最终一致性/事件驱动)。

# 114)仓储模式(Repository)

面试官: EF Core 的 DbSet 已经是仓储了,为什么还要再包一层 Repository?
候选人(要点): 解耦特定 ORM,提供领域语义的方法(FindActiveUsers 而不是 Where(x=>...)),便于 Mock 测试。但对于简单 CRUD,直接用 DbContext 也是可接受的(Pragmatic DDD)。
面试官追问: IQueryable 能从 Repository 泄露出去吗?(不建议,泄露了数据库实现细节)。

# 115)工作单元(Unit of Work)

面试官: UoW 解决了什么问题?它和 Repository 怎么配合?
候选人(要点): 管理事务,确保多个 Repository 的操作在一次提交中原子完成。通常 DbContext 本身就是 UoW 实现。
面试官追问: 跨库/跨服务的 UoW 怎么做?(分布式事务 / TCC)。

# 116)领域事件(Domain Event)

面试官: 领域事件和普通消息队列的区别?什么时候触发?
候选人(要点): 领域事件是业务发生的客观事实("UserRegistered"),用于解耦聚合间逻辑。触发时机:通常在事务提交前(进程内副作用)或提交后(发 MQ)。
面试官追问: 怎么保证事件发送与数据库提交的原子性?(Outbox 模式)。

# 117)领域服务(Domain Service)

面试官: 什么时候需要领域服务?和应用服务(Application Service)的区别?
候选人(要点): 当逻辑涉及多个聚合且无法归属于任一聚合时(如"转账")。应用服务负责编排(加载、调域服务、保存、发邮件),不含核心业务规则;领域服务含核心规则。
面试官追问: 领域服务可以调仓储吗?(可以,但尽量只依赖抽象)。

# 118)CQRS(命令查询职责分离)

面试官: 为什么要读写分离模型?CQRS 一定要双数据库吗?
候选人(要点): 读写需求不同(写重一致性,读重查询灵活性)。不一定双库,可以单库 + 读写模型分离(写实体,读 DTO);进阶才是双库(同步/异步)。
面试官追问: 异步 CQRS 的数据延迟(最终一致性)怎么处理用户体验?(前端乐观更新 / 轮询)。

# 119)六边形架构 / 洋葱架构

面试官: 核心思想是什么?依赖方向是怎样的?
候选人(要点): 依赖反转:外层(UI/DB)依赖内层(Domain),核心业务逻辑不依赖任何基础设施。通过端口(Port)和适配器(Adapter)交互。
面试官追问: Clean Architecture(整洁架构)和它们的区别?(本质一样,分层命名不同)。

# 120)微服务拆分原则

面试官: 什么时候不该拆微服务?拆分过细有什么后果?
候选人(要点): 业务不清晰、团队小、基础设施弱时不拆。后果:分布式事务噩梦、延迟增加、运维复杂度爆炸、排障困难。
面试官追问: "按技术拆"(如日志服务、鉴权服务)好还是"按业务拆"(订单、库存)好?(优先按业务领域拆)。

# 🟡 中级梯队:Redis 高级特性与分布式锁深度(121–150)[1]

# 121)Redis 线程模型深度

面试官: 为什么 Redis 6.0 引入了多线程?这是否违背了"单线程"设计?
候选人(要点): 核心命令执行仍是单线程(保证无锁、简单);多线程仅用于网络 IO(读写 Socket 缓冲区)和协议解析,解决网络带宽瓶颈。
面试官追问: 如果你的 Redis CPU 飙升,但 QPS 不高,可能是什么原因?(O(N) 慢查询、大 Key 序列化)。

# 122)Bitmap 与 HyperLogLog

面试官: 怎么在 Redis 里统计"亿级日活用户"(UV)?内存占用怎么估算?
候选人(要点):

  1. Bitmap:适合连续 ID,精确,1 亿用户约 12MB。
  2. HyperLogLog:适合非数字 ID 或巨量数据,允许误差(0.81%),固定 12KB。
    面试官追问: 怎么统计"连续 7 天登录的用户"?(Bitmap 做位运算 BITOP AND)。

# 123)GeoHash 原理与应用

面试官: GEOADD 底层存的是什么结构?怎么实现"附近的人"?
候选人(要点): 底层是 ZSet。经纬度经过 GeoHash 算法转为 52 位整数(score),利用 ZSet 的范围查询(RangeByScore)查找相邻区域点。
面试官追问: 边界问题(在格子边缘)怎么解决?(同时查询周围 8 个格子)。

# 124)Bloom Filter(布隆过滤器)插件

面试官: RedisModule(如 RedisBloom)比自己写 BitMap 实现布隆过滤器好在哪?
候选人(要点): 性能更高(C实现)、支持动态扩容(Scalable Bloom Filter)、API 封装更好(BF.ADD / BF.EXISTS)。
面试官追问: 布隆过滤器能删除元素吗?为什么?(传统的不行,Counting Bloom Filter 可以但耗内存)。

# 125)Stream(消息队列)

面试官: Redis 5.0 的 Stream 和 List/PubSub 做队列的区别?
候选人(要点):

  • List:无法多播,ACK 麻烦。
  • PubSub:消息不持久化,发后即焚。
  • Stream:支持持久化、消费者组(Consumer Group)、ACK、消息回溯,类似 Kafka 轻量版。
    面试官追问: Stream 消息堆积会撑爆内存吗?怎么限制长度?(XADD ... MAXLEN ~ 1000)。

# 126)Lua 脚本的原子性与限制

面试官: Lua 脚本能保证原子性,那它是事务吗?脚本里能写死循环吗?
候选人(要点): 保证原子性(脚本执行期间不执行其他命令)。不是 ACID 事务(中途报错不回滚)。默认有超时时间(5s),死循环会阻塞 Redis 导致不可用。
面试官追问: SCRIPT LOAD 和 EVALSHA 是为了解决什么问题?(减少网络带宽,缓存脚本)。

# 127)Keys 淘汰策略深度

面试官: allkeys-lru 和 volatile-lru 选哪个?LRU 算法是标准的吗?
候选人(要点): 如果做缓存(热数据)用 allkeys-lru;如果既做缓存又做持久存储(带 TTL 的才删)用 volatile-lru。Redis 是近似 LRU(随机采样对比),为了省内存。
面试官追问: LFU(Least Frequently Used)策略解决了 LRU 什么痛点?(防止"一次性扫描"数据挤掉热数据)。

# 128)BigKey 治理

面试官: 怎么发现 BigKey?删除 1GB 的 Hash Key 会发生什么?怎么优雅删除?
候选人(要点): 发现:redis-cli --bigkeys 或分析 RDB。直接 DEL 会阻塞主线程(STW)。优雅删:UNLINK(异步删除,Redis 4.0+)或 HSCAN 分批删。
面试官追问: 大 Key 对网络和客户端有什么影响?(Head-of-line blocking,超时)。

# 129)HotKey(热点 Key)治理

面试官: 比如"某明星出轨",所有请求都在读同一个 Key,网卡打爆。怎么解?
候选人(要点):

  1. 本地缓存(LocalCache):应用层挡住。
  2. 读写分离/副本:多 Slave 分担(不够)。
  3. 热点散列:把 Key 复制多份(Key_1, Key_2...),客户端随机访问。
    面试官追问: 怎么自动发现热点 Key?(客户端统计 / Proxy 层统计 / Redis 4.0 hotkeys 参数)。

# 130)RDB vs AOF 灾难恢复实战

面试官: 机器断电,RDB 和 AOF 文件都损坏了怎么办?
候选人(要点): 先尝试 redis-check-aof --fix 修复(会丢部分数据)。生产环境通常开启 混合持久化(RDB 头 + AOF 尾),兼顾速度与安全。
面试官追问: bgrewriteaof 期间主进程有写入,怎么保证 AOF 不丢?(AOF 重写缓冲区)。

# 131)主从复制(Replication)原理

面试官: Slave 刚连上 Master 发生了什么?什么是 PSYNC?
候选人(要点): 全量复制:Master BGSAVE 生成 RDB -> 发给 Slave -> Slave 加载。PSYNC(部分复制):断线重连后,根据 Offset 和 RunID 补发积压缓冲区(repl_backlog)里的数据。
面试官追问: 如果 repl_backlog 太小会怎样?(频繁触发全量复制,死循环)。

# 132)Sentinel(哨兵)选主逻辑

面试官: Master 挂了,哨兵怎么选出新 Master?
候选人(要点): 1) 过滤掉不健康的;2) 选优先级高的;3) 选复制偏移量(Offset)最大的(数据最全);4) 选 RunID 最小的。
面试官追问: 脑裂(Split-brain)是什么?怎么配置 min-slaves-to-write 防止数据丢失?

# 133)Cluster 重定向与 Hash Tag

面试官: 客户端请求 Key,节点返回 MOVED 和 ASK 有什么区别?
候选人(要点): MOVED:槽(Slot)永久迁移了,客户端更新映射;ASK:槽正在迁移中(临时访问目标节点)。
面试官追问: 怎么保证 user:{100}:profile 和 user:{100}:orders 落在同一个节点?(Hash Tag {})。

# 134)Client-side Caching(客户端缓存)

面试官: Redis 6.0 的客户端缓存是怎么保证一致性的?
候选人(要点): 服务端记录客户端访问了哪些 Key;当 Key 修改时,服务端给客户端发失效消息(Invalidation Message);客户端收到后清空本地缓存。
面试官追问: 广播模式(Broadcasting)的优缺点?(无需服务端记状态,但流量大)。

# 135)Redis 事务(ACID 假象)

面试官: MULTI / EXEC 能保证 ACID 吗?如果中间一条命令报错,后面会执行吗?
候选人(要点): 语法错误(入队失败)会全体取消;运行时错误(如对 String 做 LPUSH)该条失败,后续继续执行。不支持回滚(No Rollback)。
面试官追问: WATCH 命令是做什么的?(乐观锁,CAS)。

# 136)分布式锁:Redlock 争议

面试官: Redlock 到底安全吗?NPC 问题(Network, Pause, Clock)指什么?
候选人(要点): 有争议。安全隐患:时钟跳变导致锁提前过期;GC 暂停导致锁过期但客户端以为持有。结论:对于追求绝对一致性的(如金融),Redlock 不够,请用 ZooKeeper/Etcd。对于一般业务,Redlock/单实例锁够用。
面试官追问: Fencing Token 是什么?怎么解决锁过期后的并发写?

# 137)分布式锁:可重入性

面试官: Redis 锁怎么实现可重入(同一个线程多次加锁)?
候选人(要点): Value 里存 UUID:ThreadId;Hash 结构存 锁标识 和 重入计数。加锁:key 存在且是自己,计数+1;解锁:计数-1,为 0 时 DEL。
面试官追问: 可重入锁的 WatchDog 怎么处理?(只对顶层锁续期)。

# 138)分布式锁:惊群效应

面试官: 1000 个线程抢锁,释放时怎么避免同时唤醒?
候选人(要点): Redis 锁通常是自旋(Spin)或休眠重试,不涉及 OS 级唤醒惊群。但高频轮询会打挂 Redis。优化:Pub/Sub 通知机制(Redisson 做法),释放时发消息,等待者收到才抢。
面试官追问: Redisson 的 tryLock 等待机制原理?(Semaphore + PubSub)。

# 139)Redisson 原理深度

面试官: Redisson 怎么实现"锁续期"?如果客户端宕机,看门狗会死锁吗?
候选人(要点): 客户端起一个 TimerTask(默认 10s),定期发 Lua 脚本续 TTL。如果客户端宕机,Timer 停了,续期停止,Redis 里的 Key 到期自动删除,不产生死锁。
面试官追问: 业务手动把锁删了,看门狗还在跑怎么办?(Redisson 内部会维护锁状态,本地释放时同时取消 Timer)。

# 140)Redis 内存碎片

面试官: INFO memory 里的 mem_fragmentation_ratio 高说明什么?怎么处理?
候选人(要点): 说明碎片率高(申请 1G,OS 分配 1.5G)。原因:频繁修改 Key 长度、过期删除。处理:重启(最快),或配置 activedefrag 自动整理(Redis 4.0+,耗 CPU)。
面试官追问: 使用 Jemalloc 分配器比 glibc 好在哪?

# 141)Redis 慢查询日志

面试官: 怎么抓取线上慢查询?slowlog-log-slower-than 设多少合适?
候选人(要点): slowlog get 10。阈值通常设 10ms 或 1ms(视业务敏感度)。注意:Slowlog 只记录执行时间,不含网络排队时间。
面试官追问: 为什么有时候 Redis 很慢,但 Slowlog 是空的?(网络延迟、AOF 刷盘阻塞)。

# 142)Redis 协议(RESP)

面试官: 为什么 Redis 自定义 RESP 协议而不用 JSON/HTTP?
候选人(要点): 解析极快(前缀长度读取)、人类可读(文本协议)、二进制安全(Bulk String)。
面试官追问: 你能手写一个 SET key val 的 RESP 报文吗?(*3\r\n$3\r\nSET\r\n...)。

# 143)缓存预热

面试官: 大促前怎么做缓存预热?
候选人(要点): 1) 脚本批量加载热数据;2) CDN 预热;3) 应用启动时预加载。
面试官追问: 预热时怎么避免网络风暴?(分批、随机延迟)。

# 144)多级缓存架构

面试官: 本地缓存(In-Process)+ Redis + DB,怎么保证一致性?
候选人(要点): 难保证强一致。变更时:改 DB -> 发 MQ/PubSub -> 各实例清除本地缓存 + 删 Redis。接受短时间不一致(TTL兜底)。
面试官追问: 什么时候需要引入 Nginx 共享字典缓存?

# 145)Redis 运维命令

面试官: FLUSHALL、KEYS * 在生产环境怎么禁掉?
候选人(要点): redis.conf 里 rename-command FLUSHALL ""。
面试官追问: 怎么在不停止服务的情况下将 Redis 数据迁移到新集群?(主从同步 / MIGRATE / 第三方工具 redis-shake)。

# 146)Codis vs Redis Cluster

面试官: 以前常用的 Codis 和官方 Cluster 区别?现在还用 Codis 吗?
候选人(要点): Codis 是中心化 Proxy 架构,运维简单但不支持部分命令;Cluster 去中心化,客户端直连。现在主流用 Cluster 或云厂商托管版。
面试官追问: Proxy 架构(如 Twemproxy/Predixy)的好处?(客户端解耦,连接复用)。

# 147)Redis 作为数据库的坑

面试官: 把 Redis 当主库(全量存储)用,有什么风险?
候选人(要点): 内存贵;数据可靠性弱(RDB/AOF 都有丢数据窗口);复杂查询支持差;数据模型迁移困难。
面试官追问: 有什么场景适合 Redis 当主库?(Session、非关键配置、临时验证码)。

# 148)List 作为消息队列的坑

面试官: 用 BLPOP 做队列,消费者挂了消息会丢吗?怎么解决?
候选人(要点): 会丢(弹出即删除)。解决:RPOPLPUSH(原子弹出并推入备份队列),处理完再删备份。
面试官追问: 只有 List 怎么做延时队列?(不行,得配合 ZSet)。

# 149)Redis 脑裂与数据丢失

面试官: 哨兵模式下,Master 假死,Sentinel 选了新主,旧 Master 恢复了会怎样?
候选人(要点): 旧 Master 变成 Slave,同步新主数据,导致假死期间客户端写入旧主的数据全部丢失。
面试官追问: 怎么减少这种丢失?(配置 min-slaves-to-write,少于 N 个从节点确认就不让写,牺牲可用性换一致性)。

# 150)Redis 模块开发(ReModule)

面试官: 你知道 Redis 可以加载动态链接库扩展功能吗?举个例子。
候选人(要点): loadmodule。例子:RedisSearch(全文搜)、RedisJSON(原生 JSON 操作)、RedisGraph。
面试官追问: 模块的性能风险?(可能阻塞主线程)。

# 🟡 中级梯队:RabbitMQ 可靠性与性能调优(151–180)[1]

# 151)RabbitMQ 架构模式:镜像队列 vs Quorum Queues

面试官: 为什么 RabbitMQ 3.8+ 推荐用 Quorum Queues 代替镜像队列(Mirrored Queues)?
候选人(要点): 镜像队列同步效率低(全量同步)、网络分区处理差(脑裂风险);Quorum Queues 基于 Raft 协议,只同步日志,支持大多数提交,性能和可靠性更好,天然支持毒消息处理。[2]
面试官追问: 迁移到 Quorum Queues 有什么限制?(不支持 TTL、不支持排他队列、内存开销稍大)。

# 152)消息可靠性:Publisher Confirm 原理

面试官: 开启 Publisher Confirm 后,Broker 什么时候发回 Ack?是落盘后吗?
候选人(要点): 普通消息:投递到所有匹配队列后(如果队列持久化则需落盘);镜像消息:所有镜像接收/落盘后。这是异步的。
面试官追问: 如果 Confirm 没收到(Nack),生产者应该怎么做?(重试发送,注意去重)。

# 153)消息可靠性:Return Listener

面试官: 消息发到 Exchange 但找不到 Queue(RoutingKey 没配对),消息去哪了?怎么感知?
候选人(要点): 默认丢弃。开启 Mandatory=true 并注册 Return Listener,Broker 会把不可达消息退回给生产者。
面试官追问: 备份交换机(Alternate Exchange)的作用?它和 Return Listener 谁优先级高?

# 154)消息积压:消费者扩容与预取

面试官: 线上积压 100 万条,除了加机器,消费者参数 prefetch_count 怎么调?
候选人(要点): 调大 prefetch(如 100-500),配合多线程消费/批量消费,减少网络 ACK 交互频率;但不能太大导致单节点内存溢出或负载不均。
面试官追问: 如果积压是因为某一条毒消息导致消费者无限崩溃重启,怎么救?

# 155)死信队列(DLX)循环引用

面试官: 如果死信队列 TTL 过期后又转发回原队列,会发生什么?这种模式有什么用?
候选人(要点): 构成死循环。用途:实现延迟重试队列(消费失败 -> 进死信等待 5s -> 回原队列重试)。
面试官追问: RabbitMQ 怎么识别"这条消息重试了多少次"?(查看 header 里的 x-death 数组)。

# 156)延迟队列实现对比

面试官: TTL + DLX vs rabbitmq-delayed-message-exchange 插件,选哪个?
候选人(要点): 插件更方便(基于 Mnesia 表),支持任意延迟时间;TTL+DLX 需要建多个固定时间的死信级联。推荐插件,但注意插件在大数据量下性能较差(消息存在 RAM)。
面试官追问: 为什么不推荐用应用层 Task.Delay 做延迟?(不持久、重启丢失、占内存)。

# 157)消息顺序性:Sharding

面试官: 只有 1 个 Queue 吞吐量不够,多 Queue 又乱序,怎么解?
候选人(要点): 分片(Sharding)。使用 Consistent Hash Exchange 或应用层 Hash,把同一 OrderId 的消息总是路由到同一个 Queue,该 Queue 对应一个消费者,实现局部顺序 + 并行扩容。
面试官追问: 扩容增加 Queue 时,旧消息顺序会乱吗?怎么平滑迁移?

# 158)RabbitMQ 内存控制(Memory High Watermark)

面试官: RabbitMQ 内存告警(阻塞生产者)的阈值默认是多少?达到阈值会发生什么?
候选人(要点): 默认 40% 物理内存。达到后会停止接收新消息(Connection Blocked),直到内存水位下降(GC 或刷盘)。
面试官追问: 怎么避免这种情况?(开启 Lazy Queue、及时消费、调大阈值但风险增加)。

# 159)Lazy Queue(惰性队列)

面试官: Lazy Queue 和普通队列的区别?什么时候必须用?
候选人(要点): 普通队列尽量存内存;Lazy Queue 直接存磁盘(只在内存留索引)。适合:大量积压场景、冷备队列、内存紧张的环境。
面试官追问: Lazy Queue 的性能代价?(磁盘 IO 增加,延迟稍高)。

# 160)RabbitMQ 集群:普通集群 vs 镜像集群

面试官: 普通集群模式下,队列数据是在所有节点都有吗?
候选人(要点): 不是。元数据(Exchange/Queue 定义)全量同步,但消息实体只在创建队列的节点(Owner Node)。其他节点只转发请求。
面试官追问: 这种模式有什么单点风险?镜像集群是怎么解决的?

# 161)脑裂(Network Partition)处理

面试官: RabbitMQ 集群发生网络分区(脑裂)时,默认行为是什么?怎么配置自动恢复?
候选人(要点): 默认 ignore(各分区独立写入,合并时数据冲突丢失)。推荐配置 pause_minority(暂停少数派节点)或 autoheal(胜者为王,丢弃败者数据)。可靠性要求高选 pause_minority。
面试官追问: CAP 理论里 RabbitMQ 是 AP 还是 CP?(镜像队列偏 AP,Quorum Queues 偏 CP)。

# 162)消息幂等:全局 ID 生成

面试官: 消息体里没有业务唯一键(如日志),怎么做幂等?
候选人(要点): 生产者生成 UUID / Snowflake ID 放入 Message Properties (MessageId);消费者根据此 ID 去重。不要依赖 Broker 生成的 ID(那个无法跨重试保持一致)。
面试官追问: 消费者去重表的数据量太大会影响性能吗?怎么定期清理?

# 163)优先级队列(Priority Queue)

面试官: 怎么实现 VIP 用户消息优先处理?RabbitMQ 支持插队吗?
候选人(要点): 支持 x-max-priority 参数定义优先级队列。发送时设置 Priority 属性。高优先级优先被消费。
面试官追问: 优先级队列的性能损耗?如果队列一直没积压,优先级生效吗?(不生效,先进先出)。

# 164)RPC 模式(Request-Reply)

面试官: 用 RabbitMQ 做 RPC(同步调用)的原理?
候选人(要点): 生产者发消息带 ReplyTo 队列名和 CorrelationId;消费者处理完把结果发回 ReplyTo 队列;生产者阻塞等待该队列响应。
面试官追问: 如果 RPC 超时了怎么处理?CorrelationId 的作用?

# 165)Federation 与 Shovel

面试官: 怎么实现跨机房/跨区域的消息同步?
候选人(要点):

  • Federation:逻辑连接,下游主动拉取上游消息,适合跨 WAN 扩展。
  • Shovel:类似转发器,从源队列搬运到目的队列,更灵活(支持不同 Broker 类型)。
    面试官追问: Federation 环形拓扑会导致消息无限循环吗?(不会,有跳数限制)。

# 166)MassTransit / CAP 框架

面试官: 在 .NET 里直接用 RabbitMQ.Client 还是用封装框架(MassTransit/CAP)?为什么?
候选人(要点): 推荐封装框架。MassTransit 封装了重试、熔断、序列化、Saga;CAP 封装了本地消息表(Outbox)保证最终一致性。原生 Client 容易写出 Bug(如连接复用、异常处理不当)。[3]
面试官追问: CAP 的工作原理?它怎么保证"数据库提交成功,消息一定发出"?

# 167)Exchange to Exchange Binding

面试官: 交换机能绑定交换机吗?有什么用?
候选人(要点): 能。用于构建复杂路由拓扑,减少重复绑定。例如:一个 LogExchange 广播给 FileExchange 和 DbExchange,解耦不同类型的处理管道。
面试官追问: 这样会增加路由延迟吗?

# 168)Connection vs Channel

面试官: 一个应用该创建多少 Connection?多少 Channel?
候选人(要点): Connection 对应 TCP 连接,很重,整个应用应单例(复用);Channel 对应虚拟会话,轻量,非线程安全,每个线程/并发任务应独占一个 Channel。
面试官追问: Channel 也能复用吗?多线程共用一个 Channel 会报什么错?

# 169)Prefetch Count 调优

面试官: BasicQos(prefetch=1) 是什么意思?高吞吐场景设多少?
候选人(要点): 设为 1 表示"处理完 1 个才发下 1 个"(公平分发),适合耗时任务。高吞吐场景应设大(如 100),配合批量 ACK,减少网络 RTT 等待。
面试官追问: 如果消费者是多线程并发处理,prefetch 应该怎么设?(线程数 * N)。

# 170)消息体大小限制

面试官: 能用 MQ 传 100MB 的文件吗?最大限制是多少?
候选人(要点): 理论无硬限(受内存限制),但建议 < 128KB。大文件应传 URL(对象存储地址),MQ 只传引用。大消息会阻塞带宽,导致心跳超时,引发连接断开。
面试官追问: 如果必须传大文本,怎么优化?(Gzip 压缩)。

# 171)RabbitMQ 监控指标

面试官: 线上出问题,你主要看哪些监控指标?
候选人(要点):

  1. Queue Depth(堆积量)。
  2. Unacked Messages(正在处理量,高说明处理慢或忘 ACK)。
  3. Connection/Channel Count(是否有泄漏)。
  4. Disk/Memory Alarms。
    面试官追问: Ready 很多但 Unacked 为 0,说明什么?(消费者挂了或没订阅)。

# 172)消费者限流(Rate Limiting)

面试官: 怎么限制消费速度,防止把下游 DB 打挂?
候选人(要点): 设置 prefetch_count;在消费者业务逻辑里加令牌桶(RateLimiter);或者简单地 Thread.Sleep(不推荐)。
面试官追问: 暂停消费(StopConsuming)怎么实现?(BasicCancel 取消订阅)。

# 173)RabbitMQ 插件机制

面试官: 除了延迟插件,还用过什么插件?Management 插件是默认开的吗?
候选人(要点): Management(管理界面)、Sharding(分片)、Consistent Hash。生产环境通常需要显式启用。
面试官追问: 插件会影响性能吗?(会,尤其是涉及持久化或复杂逻辑的)。

# 174)临时队列(Temporary Queue)

面试官: RPC 里的 ReplyTo 队列通常是什么类型的?
候选人(要点): 独占(Exclusive)、自动删除(Auto-delete)、匿名队列(服务端生成随机名)。连接断开即销毁。
面试官追问: 如果客户端崩溃,临时队列没删,会怎样?(占用 Broker 资源,直到重启)。

# 175)Trace 日志(Firehose)

面试官: 怎么抓取进出 Broker 的每一条消息进行调试?
候选人(要点): 开启 Firehose Tracer;它会把所有消息复制一份发到 amq.rabbitmq.trace 交换机。注意:生产开启会严重影响性能。
面试官追问: GUI 版的 tracing 工具是什么?

# 176)消息版本控制

面试官: 消费者代码升级了,消息格式变了,怎么做平滑发布?
候选人(要点):

  1. 消息体带版本号 v=2。
  2. 消费者兼容旧版本(宽容读取)。
  3. 蓝绿部署:新消费者绑定新队列,Exchange 路由切换。
    面试官追问: Protobuf 相比 JSON 在版本兼容上的优势?

# 177)事务消息(TX)

面试官: RabbitMQ 支持事务吗(txSelect, txCommit)?性能怎么样?
候选人(要点): 支持 AMQP 协议级事务,但性能极差(同步阻塞),降低吞吐量 2-10 倍。基本不用,用 Publisher Confirm 代替。
面试官追问: 为什么 Confirm 比事务快?(异步流式)。

# 178)vhost(虚拟主机)

面试官: vhost 的作用?多租户怎么隔离?
候选人(要点): 逻辑隔离(类似 Namespace),拥有独立的 Exchange/Queue/权限。不同环境(Dev/Test)或不同租户应使用不同 vhost。
面试官追问: vhost 之间能路由消息吗?(不能,完全隔离)。

# 179)Erlang Cookie

面试官: 集群搭建时 Erlang Cookie 不一致会报什么错?
候选人(要点): 认证失败,节点无法通信(Connection refused / Authentication failed)。必须保证所有节点 Cookie 文件内容完全一致。
面试官追问: 只有 Cookie 一致就能组网吗?(还需要主机名解析正常)。

# 180)RabbitMQ 运维:节点下线

面试官: 怎么优雅下线一个集群节点进行维护?
候选人(要点):

  1. rabbitmqctl stop_app。
  2. 检查队列镜像同步状态,确保数据已迁移到其他节点。
  3. 从负载均衡摘除。
    面试官追问: 如果直接 kill 进程,Quorum Queue 会受影响吗?(Raft 会重新选主,短暂不可用但数据安全)。
编辑 (opens new window)
#中级 底层
上次更新: 2026/03/05, 07:03:39
补充
进阶

← 补充 进阶→

最近更新
01
鉴权服务中心
03-11
02
聚合根
03-11
03
补充
02-06
更多文章>
Theme by Vdoing | Copyright © 2019-2026 moklgy's blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式