Skip to content

微服务面试题 - 2026-06-19 16:54 ​

生成时间:2026-06-19 16:54 | 共 10 题 | 难度递进(基础→进阶→高阶)


【面试题】1. 微服务拆分有哪些常见策略?按业务能力拆分和按子域拆分有什么区别? ​

【标准答案】拆分策略主要有三种:按业务能力拆分(看组织结构和业务流程)、按子域拆分(DDD的限界上下文)、按技术边界拆分(读多写少可独立)。按业务能力是"看公司怎么干活就怎么拆",比如订单服务、支付服务就是按业务能力划分;按子域是DDD的玩法,先识别核心域、支撑域、通用域,再做限界上下文映射,更精细但分析成本更高。实际项目里通常是两者结合:先按业务能力做大粒度拆分,核心域内部再用DDD做更细粒度的拆分。关键教训是别一上来就拆太细——拆多了就是分布式单体,通信开销比收益还大,每个阶段拆完跑一段时间验证再拆下一个。


【面试题】2. 可观测性三大支柱 Logging、Metrics、Tracing 分别解决什么问题?只上 ELK 够不够? ​

【标准答案】Logging 回答"出了什么事"——记录离散事件,排查具体错误时看日志;Metrics 回答"趋势怎么样"——聚合监控指标,比如 QPS、P99延迟、错误率,用来建告警看大盘;Tracing 回答"全链路怎么走的"——一次请求跨 N 个服务,看耗时瓶颈到底在哪个环节。只上 ELK 远远不够:日志是离散快照,看不出趋势(你不知道延迟是在缓慢恶化还是某个时间点突然崩了),也看不出跨服务的调用链路(一个慢请求到底慢在哪个服务)。正确的做法是三个都要有——ELK/CLS 做日志、Prometheus 做指标、SkyWalking/Jaeger 做链路,三者组合才构成完整可观测性体系。


【面试题】3. 分布式缓存中,缓存穿透、缓存击穿、缓存雪崩分别是什么?各自怎么防? ​

【标准答案】缓存穿透:查一个不存在的数据,缓存没命中,请求直接打到 DB。高频查不存在的 key 能把 DB 打爆。方案:布隆过滤器(提前拦截不存在的 key)+ 空值缓存(null 也存,短 TTL 防内存浪费)。缓存击穿:一个热点 key 过期瞬间,大量并发请求同时打到 DB。方案:互斥锁(第一个请求去加载,其他等结果)+ 逻辑过期(热点 key 不设 TTL,后台异步刷新)。缓存雪崩:大量 key 同时过期或缓存集群挂了,请求瞬间全部压垮 DB。方案:过期时间加随机偏移(别让 key 扎堆 expire)+ 多级缓存(本地 Caffeine + 远程 Redis)+ 限流降级兜底。三者名字像但根因完全不同,面试官就是考你能不能分清。


【面试题】4. 分布式链路追踪的核心原理是什么?TraceId、SpanId、ParentSpanId 怎么串联起一条完整链路? ​

【标准答案】核心原理就两步:生成上下文 + 传播上下文。一条请求链路叫 Trace,由多个 Span 组成(每个 Span 代表一次服务内操作或一次 RPC 调用)。每个 Span 包含 TraceId(全局唯一,标识整条链路)、SpanId(当前 Span 自身的 ID)、ParentSpanId(上游父 Span 的 ID),三者构成父子树结构。传播机制:服务 A 调用服务 B 时,A 通过 HTTP Header(如 W3C traceparent、b3)或 RPC 透传字段把 TraceId + SpanId 传给 B,B 收到后把 A 的 SpanId 当作自己的 ParentSpanId,生成新的 SpanId,继续往下游传。最终所有 Span 上报到 SkyWalking/Zipkin 后端按 TraceId 聚合,靠 SpanId/ParentSpanId 还原完整调用树。整个过程靠探针字节码增强自动完成,对业务代码零侵入。


【面试题】5. TCC 分布式事务的 Try-Confirm-Cancel 三个阶段分别做什么?和 2PC 的本质区别在哪? ​

【标准答案】Try:预留资源和前置检查。比如转账场景,Try 阶段把转出方余额冻结,校验余额是否足够,但不真正扣钱。Confirm:确认提交,各服务真正执行业务。把冻结的余额扣除,增加目标账户余额。Cancel:回滚释放,如果任何一个环节失败,把 Try 阶段预留的资源全部释放(解冻金额)。TCC 和 2PC 的本质区别:2PC 是数据库层面的强一致协议,所有参与者加行锁等协调者拍板,性能差、锁持有时间长,容易出现阻塞;TCC 是业务层面的补偿方案,不持有数据库锁,资源隔离靠业务状态(如"冻结"状态),性能好得多。代价也很明显——你需要为每个业务接口都实现 Try、Confirm、Cancel 三套逻辑,业务代码侵入大。


【面试题】6. CQRS 命令查询职责分离解决了什么问题?什么场景值得引入,什么场景是过度设计? ​

【标准答案】CQRS 把写操作(Command)和读操作(Query)分离到不同模型甚至不同数据库。写模型专注业务规则和领域逻辑,保证数据一致性;读模型按查询需求定制化构建,可以反范式、预聚合、建宽表。核心价值是读写不再互相掣肘——写用严格的事务模型,读可以做各种物化视图优化性能。值得引入的场景:读写比例极度不平衡(读远多于写)、查询维度灵活多变需要组合不同视图、事件溯源天然适合审计和回溯系统。过度设计的场景:标准 CRUD 业务,读就是简单 select,写就是 insert/update,引入 CQRS 反而多维护一套数据同步管道——本来一个 ORM 搞定的事,变成事件总线 + 数据同步 + 双写一致性一堆问题,得不偿失。


【面试题】7. 微服务里跨服务数据一致性有哪些通用解法?如何理解"最终一致性"≠"数据会丢"? ​

【标准答案】主流解法分三类:① 同步强一致——2PC/Seata AT 模式,适合一致性要求极高、并发不高的场景(比如金融核心转账记录);② 异步最终一致——本地消息表 + 定时轮询、RocketMQ 事务消息、CDC(canal 监听 binlog 投递 MQ),适合大多数互联网业务;③ 补偿型——TCC/SAGA,业务自己实现回滚逻辑,灵活性最高但开发成本也最高。"最终一致性"不是"数据会丢"——它是"数据最终一定会到达一致状态",只是有延迟。工程上要做到三个保障:消息必达(MQ 重试 + 死信队列兜底)、补偿必有(回滚/正向补偿逻辑完整覆盖)、幂等必真(每个接口都能安全重入不会产生副作用)。这三个做到位,最终一致性在工程上就是可靠、可验证的。


【面试题】8. Resilience4j 相比 Hystrix 和 Sentinel 的设计哲学有什么不同?函数式编程风格带来什么优势? ​

【标准答案】Resilience4j 的设计哲学是"轻量级组合式"——不搞大而全的框架,而是把熔断、限流、重试、隔离、超时拆成独立小模块,开发者按需链式组合(装饰器模式)。对比 Hystrix:Hystrix 已停更,默认线程池隔离,每个命令一个线程池资源开销大,架构是"一刀切";Resilience4j 默认信号量隔离,轻量得多。对比 Sentinel:Sentinel 是"规则驱动"的运维友好型,控制台 + Dashboard + 动态规则治理一整套,适合中大型团队;Resilience4j 是"代码驱动"的开发友好型,配置用 Fluent API 直接在代码里写,适合中小团队或不想引入额外基础设施的项目。函数式风格的优势:链式声明式编排(一行链式调用把限流+熔断+重试全配上)、原生支持 CompletableFuture 和 Reactor、代码即文档可读性高。


【面试题】9. DDD 里聚合(Aggregate)设计的核心原则是什么?如何避免设计出"超大聚合"? ​

【标准答案】四个核心原则:① 一致性边界——聚合内部强一致,聚合之间只能最终一致,用领域事件解耦;② 通过根实体访问——所有外部操作必须走聚合根,不能绕过它直接改内部实体;③ 小聚合优先——聚合越小越好,尽量做到一个聚合只有一个实体,足够理由才扩大边界;④ 引用其他聚合用 ID——聚合之间不要持有对象引用,用唯一 ID 做关联,天然解耦。避免超大聚合的实用方法:不要一看到"订单和订单明细"就天然认为是一个聚合。经验判断——订单创建后,订单明细很少和订单同时被修改(明细通常只读),那就可以拆成两个聚合,订单通过 ID 引用明细。判断标准很简单:两个实体是不是必须在一个事务里同时被修改?不是就拆开。另一个技巧是用"业务流程"而不是"数据模型关系"来切分聚合边界。


【面试题】10. 从 0 到 1 接手一个新项目,要从单体迁移到微服务,你的分阶段方案是什么? ​

【标准答案】第一阶段(奠基):先上基建——注册中心(Nacos)、配置中心、网关、统一日志/监控/链路追踪。别急着拆服务,先把可观测性和治理能力建起来,否则拆完就是运维地狱。第二阶段(抽离):拆"无状态 + 低耦合"的服务——优先拆网关层后面的独立模块,如用户认证、文件服务、通知服务。用"绞杀者模式":新功能走微服务,老代码不动,逐步替换。第三阶段(核心拆分):拆核心业务域,选读多写少、业务边界清晰的模块先下手。每次只拆一个服务,拆完跑一周验证稳定性再拆下一个。第四阶段(持续优化):灰度发布、全链路压测、SLA 治理、服务编排。三条红线必须守住:① 数据库不共享,每个服务独享数据源;② 同步调用链不超过 3 层,超了就改异步解耦;③ 每个阶段都要有可执行的回滚方案。最后记住:迁移是手段不是目的,如果业务量不大,单体 + 模块化远比强行微服务靠谱。