📅 2026-07-27 | 🏷️ Java · 后端方向 | ⏱️ 建议 5h | 🎯 后端面试的终极考验——分布式系统设计能力

📌 今日知识地图

分布式 + MQ + 微服务 面试全景
│
├── 模块一:分布式理论
│   ├── CAP 理论 & BASE 理论
│   ├── 一致性协议(Paxos / Raft)
│   └── 分布式 ID(雪花算法 / 号段模式)
│
├── 模块二:分布式锁 & 分布式事务
│   ├── Redis 分布式锁 vs ZooKeeper 分布式锁
│   ├── 分布式事务:2PC → TCC → Seata-AT → MQ 最终一致性
│   └── 本地消息表 & 事务消息
│
├── 模块三:消息队列
│   ├── RabbitMQ:Exchange / 死信 / 延迟队列 / 可靠性
│   ├── Kafka:高吞吐 / 分区副本 ISR / 幂等 / 事务
│   └── RabbitMQ vs Kafka 选型
│
├── 模块四:微服务
│   ├── Nacos(注册中心 + 配置中心)
│   ├── Gateway 网关 + Sentinel 熔断限流
│   ├── 链路追踪(SkyWalking)+ 灰度发布
│   └── Docker + K8s 基础
│
└── 面试题精选(10 道 + 公司标签)

模块一:分布式理论

1.1 CAP 理论

CAP = 分布式系统最多只能同时满足其中两个:

  Consistency(一致性):所有节点同一时刻看到相同数据
  Availability(可用性):每个请求都能收到非错误的响应(但不保证数据最新)
  Partition Tolerance(分区容错):节点间网络故障时系统仍能工作

因为网络分区(P)在分布式系统中不可避免 → 必须在 C 和 A 之间取舍

CP(保一致性,牺牲可用性):
  网络分区时,不可用的分区拒绝服务
  例:ZooKeeper(Leader 宕机时暂停服务直到选举完成)、银行转账

AP(保可用性,牺牲强一致性):
  网络分区时,所有分区都能继续服务(但数据可能不一致)
  例:Eureka、DNS
  最终一致性(Eventual Consistency)是 AP 的典型实践

1.2 BASE 理论

BASE = Basically Available(基本可用)+ Soft State(软状态)+ Eventually Consistent(最终一致性)

Basically Available:系统出现故障时允许损失部分可用性
  → 响应时间变慢 / 非关键功能降级

Soft State:系统中的数据允许存在中间状态
  → 数据副本之间暂时不一致是可以接受的

Eventually Consistent:经过一段时间后,所有数据副本最终达到一致
  → 不要求实时一致,但保证最终一致

BASE 是 CAP 中 AP 的延伸:牺牲强一致性,换取更高的可用性。

1.3 Raft 一致性协议

Raft 解决什么问题?
  → 多个节点对某个值达成一致(谁的数据是对的?)

Raft 三个角色:
  Leader   :处理所有客户端请求,发送日志给 Follower(一个任期只有一个)
  Follower :被动接收 Leader 的日志,不主动发起请求
  Candidate:竞选 Leader 期间的临时角色

Raft 两个核心机制:
① Leader 选举:
   心跳超时 → Follower 变 Candidate → 任期+1 → 投自己一票 → 请求其他节点投票
   → 获得多数票 → 成为 Leader → 发送心跳维持统治
   → 如果两个 Candidate 票数相同 → 随机超时后重新选举

② 日志复制:
   客户端请求 → Leader 追加日志 → 并发给所有 Follower → 多数确认 → 提交
   → Leader 应用到状态机 → 返回结果给客户端
   → Leader 在后续心跳中通知 Follower 提交

1.4 分布式 ID

雪花算法(Snowflake):
  1bit(符号位,不用) | 41bit(毫秒时间戳,69年) | 10bit(机器ID,1024台) | 12bit(序列号,每毫秒4096个)

  优点:高性能、趋势递增(对数据库索引友好)、不依赖外部服务
  缺点:依赖机器时钟 → 时钟回拨会出问题
  时钟回拨解决:
    → 等时钟追上
    → 用"历史最大时间戳"兜底,时钟回拨期间用备用机器ID
    → 美团 Leaf:号段模式 + 雪花算法双模式

号段模式(美团 Leaf-Segment):
  从数据库批量取一段 ID(如 1~1000)→ 缓存到本地 → 用完再取下一段
  优点:无时钟依赖,ID 严格递增
  缺点:依赖数据库

模块二:分布式锁 & 分布式事务

2.1 Redis 锁 vs ZooKeeper 锁

维度RedisZooKeeper
原理SET NX EX + Lua临时顺序节点 + Watch
实现争抢式(CAS 抢锁)排队式(节点最小序号获得锁)
释放主动删除(看门狗续期)连接断开自动删除(临时节点)
一致性AP(主从异步,可能丢锁)CP(ZAB 协议,强一致)
性能极高(内存操作)中(需要 ZAB 协议同步)
适用性能敏感、允许极低概率的锁丢失一致性要求高(如金融场景)
// ZooKeeper 分布式锁原理:
// ① 所有线程在 /lock 下创建临时顺序节点(/lock/seq-0001, /lock/seq-0002, ...)
// ② 序号最小的节点获得锁
// ③ 其他节点 Watch 前一个节点 → 前一个节点释放 → 收到通知 → 成为最小 → 获得锁
// ④ 连接断开 → 临时节点自动删除 → 下一个节点自动获得锁
// 优点:公平锁(排队),连接断开自动释放,强一致性
// 缺点:性能比 Redis 低,需维护 ZK 集群

2.2 分布式事务

分布式事务的核心困难:
  一个业务操作涉及多个数据库/服务
  如何保证跨库/跨服务的 ACID?

四种方案:

┌─────────────────────────────────────────────────────────┐
│ ① 2PC(Two-Phase Commit)— XA 协议                       │
│                                                          │
│ 协调者 → 所有参与者:Prepare(预提交,锁定资源)            │
│ 协调者 → 所有参与者:Commit / Rollback                    │
│                                                          │
│ 缺点:同步阻塞、协调者单点、数据不一致风险(Commit 阶段崩了)│
│ 适用:传统数据库(MySQL XA)、JTA                         │
├─────────────────────────────────────────────────────────┤
│ ② TCC(Try-Confirm-Cancel)                              │
│                                                          │
│ Try     :预留资源(冻结库存、预扣余额)                    │
│ Confirm :提交(真正扣减)                                 │
│ Cancel  :释放(退回冻结的资源)                            │
│                                                          │
│ 优点:不阻塞、业务层实现、灵活                            │
│ 缺点:代码侵入强(每个接口都要写三套逻辑)、需幂等          │
│ 适用:金融、电商核心链路                                  │
├─────────────────────────────────────────────────────────┤
│ ③ Seata-AT(自动补偿)                                   │
│                                                          │
│ 基于 undo_log 自动生成回滚 SQL → 无业务侵入               │
│ 两阶段:① 业务 SQL + 记录 undo_log                       │
│         ② 成功 → 删 undo_log / 失败 → 用 undo_log 回滚   │
│                                                          │
│ 优点:对业务无侵入、使用简单                              │
│ 缺点:性能损耗(多一次 undo_log 写入)                    │
│ 适用:不希望改业务代码的场景                              │
├─────────────────────────────────────────────────────────┤
│ ④ MQ 最终一致性(最常用!)                               │
│                                                          │
│ 本地事务 + MQ 消息 = 最终一致                             │
│ 例:下单 → 扣库存(本地事务 + 发消息)                     │
│           → 创建订单(消费消息)                          │
│                                                          │
│ 核心:本地事务和发消息必须是原子的(事务消息/本地消息表)    │
│ 优点:高可用、高性能、解耦                                │
│ 缺点:数据不是实时一致的                                  │
│ 适用:对一致性要求不极端的场景(绝大多数互联网业务)        │
└─────────────────────────────────────────────────────────┘

2.3 本地消息表 & 事务消息

// ═══════════════════════════════════════
// 本地消息表(最经典的最终一致性方案)
// ═══════════════════════════════════════

// 思路:在同一个数据库中创建一张"消息表"
// 业务操作和消息写入在同一个本地事务中 → 保证原子性

@Transactional
public void createOrder(Order order) {
    // ① 业务操作
    orderMapper.insert(order);

    // ② 写消息表(在同一个事务中!)
    Message msg = new Message();
    msg.setTopic("ORDER_CREATED");
    msg.setBody(JSON.toJSONString(order));
    msg.setStatus("PENDING");
    messageMapper.insert(msg);

    // ③ 事务提交后 → 定时任务扫 PENDING 消息 → 发送到 MQ
    // ④ 消费者处理成功后 → 更新消息状态为 SENT
    // ⑤ 定时任务重试:PENDING 超过 N 分钟 → 重新发送
}

// ═══════════════════════════════════════
// RocketMQ 事务消息
// ═══════════════════════════════════════
// ① 发送 Half 消息(半消息,消费者不可见)
// ② 执行本地事务
// ③ 本地事务成功 → Commit → 消费者可见
//    本地事务失败 → Rollback → 消息删除
// ④ 如果生产者挂了(没 Commit 也没 Rollback)
//    → Broker 定期回调生产者检查本地事务状态(checkListener)

模块三:消息队列

3.1 RabbitMQ

核心概念:
  Producer → Exchange(交换机) → [Binding] → Queue(队列) → Consumer

Exchange 四种类型:
  Direct   :Routing Key 完全匹配 → 精确路由
  Topic    :Routing Key 通配符匹配(*匹配一个词, #匹配零或多个词)
  Fanout   :广播到所有绑定的队列(忽略 Routing Key)
  Headers  :根据 Header 匹配(很少用)

消息可靠性三件套:
  ① 生产端确认(Publisher Confirm):
       生产者 → Broker:消息收到了吗?
       Broker → 生产者:收到了(ack)/ 没收到(nack)→ 重发

  ② 消费端确认(Consumer ACK):
       消费者处理完 → 手动 ACK → Broker 删除消息
       没处理完(连接断开/异常)→ 未 ACK → Broker 重新投递

  ③ 持久化:
       Queue 持久化 + Message 持久化(delivery_mode=2)
       → Broker 重启消息也不丢

死信队列(DLX - Dead Letter Exchange):
  消息变成死信的条件:
    → 被消费者拒绝(reject/nack)且 requeue=false
    → 消息过期(TTL)
    → 队列满了

  死信队列 = "异常消息收容所" → 人工处理 / 定时任务重新投递

延迟队列:
  TTL + DLX 的经典组合:
    → 消息发到 A 队列(设置了 TTL,没有消费者)
    → 过期后变成死信 → 投递到 B 队列
    → B 队列的消费者在延迟后收到消息
  场景:订单 30 分钟未支付取消

3.2 Kafka

Kafka 为什么吞吐量这么高?

① 顺序写磁盘(Sequential Write):
   追加写 → 磁盘顺序 I/O 接近内存随机 I/O 速度
   (比随机写快 100 倍以上)

② Page Cache(页缓存):
   数据先写到 OS 的 Page Cache → 由 OS 决定何时刷盘
   读数据优先从 Page Cache 读 → 命中率高 → 不走磁盘

③ 零拷贝(Zero Copy):
   sendfile() 系统调用 → 数据从 Page Cache 直接到网卡
   → 不经过用户态 → 减少拷贝和上下文切换

④ 分区并行(Partition):
   Topic 分成多个 Partition → 每个 Partition 独立读写
   → 多个 Consumer 并行消费 → 水平扩展

⑤ 批量处理(Batching):
   生产者攒一批消息一起发 → 减少网络开销
   消费者一次拉一批 → 减少拉取次数
Kafka 核心概念:

  Topic      → 消息的逻辑分类
  Partition  → 每个 Topic 分为多个 Partition(物理分片)
               Partition 内消息严格有序,全局无序
  Replica    → 每个 Partition 有 N 个副本(1 Leader + N-1 Follower)
  ISR        → In-Sync Replicas,与 Leader 保持同步的副本集合
               如果 Follower 落后太多 → 从 ISR 中移除
  Offset     → 每条消息在 Partition 内的唯一位置编号

消息可靠性保证:
  生产者 acks:
    acks=0    → 不等待确认(最快,可能丢消息)
    acks=1    → Leader 确认即可(默认)
    acks=all  → 所有 ISR 确认(最安全,推荐)

  消费者 Offset 提交:
    自动提交 → 可能丢消息(消息拉取后自动提交,还没处理完)
    手动提交 → 处理完再提交 → 至少一次语义

  幂等性(Idempotent):
    生产者:enable.idempotence=true
      → 自动去重(Producer ID + Sequence Number)
    消费者:业务层实现幂等(唯一键 / 版本号 / Redis 去重)

  事务(Transactional):
    生产者:initTransactions → beginTransaction → send → commitTransaction
    支持跨 Partition 的原子写入

3.3 RabbitMQ vs Kafka

维度RabbitMQKafka
定位消息代理(AMQP 协议)分布式流平台
吞吐万级/秒百万级/秒
延迟微秒级毫秒级
消息回溯不支持(消费完就删)支持(按 Offset 重放)
顺序全局顺序(单队列)Partition 内有序
持久化消息持久化 + 队列持久化全部落盘(默认持久化)
推拉Push(推送)Pull(拉取,长轮询)
路由丰富(Exchange/Binding)简单(Topic 直接发)
运维轻量重(依赖 ZooKeeper/KRaft)
适用业务消息、RPC、延迟消息日志、大数据、流处理、事件溯源

模块四:微服务

4.1 微服务体系全景

┌─────────────────────────────────────────────────────────┐
│                    微服务体系架构                         │
│                                                          │
│  外部请求 → Gateway(网关) → Service A → Service B        │
│                │               │           │            │
│          Nacos(注册发现)  Nacos(配置中心)                 │
│                │               │           │            │
│          Sentinel(熔断限流)    │      SkyWalking(链路追踪)│
│                │               │           │            │
│                └───────────────┴───────────┘            │
│                              │                          │
│                        消息队列(MQ)                      │
│                              │                          │
│                    Docker + K8s 部署                    │
└─────────────────────────────────────────────────────────┘

4.2 核心组件速查

Nacos(注册中心 + 配置中心):
  注册中心:服务启动 → 注册到 Nacos → 定时心跳(5s)
           服务调用 → 从 Nacos 获取服务列表 → 负载均衡 → 调用
           服务下线 → Nacos 剔除 → 通知订阅者更新列表
  配置中心:配置修改 → Nacos 推送 → 应用实时刷新(@RefreshScope)

Gateway 网关:
  路由(Route):根据路径/Header 转发到对应服务
  过滤(Filter):鉴权、限流、日志、跨域
  断言(Predicate):匹配请求条件
  → Spring Cloud Gateway 底层:Netty + WebFlux(非阻塞)

Sentinel(熔断限流降级):
  限流:QPS 超过阈值 → 排队/拒绝(滑动窗口算法)
  熔断:错误率超过阈值 → 快速失败 → 一段时间后探测恢复
  降级:系统负载高 → 返回降级响应(默认值/静态页面)
  三种效果:快速失败 / Warm Up(预热)/ 匀速排队

SkyWalking(链路追踪):
  每个请求生成 TraceID → 在服务间传递
  → 记录每个 Span(一次服务调用)的时间和状态
  → 可视化拓扑图 + 调用链路 + 耗时分析

灰度发布:
  流量染色:Header 中打标签(如 version=v2)
  → Gateway 根据标签路由到新版本服务
  → 先放 10% 流量到 V2 → 观察 → 逐步扩大到 100%

4.3 Docker + K8s 基础

Docker:
  Dockerfile → Image(镜像)→ Container(容器)
  关键命令:
    docker build -t app:v1 .
    docker run -d -p 8080:8080 --name myapp app:v1
    docker-compose up -d  (多容器编排)
  多阶段构建:
    FROM maven AS build → 编译 → FROM openjdk AS runtime → 只复制 JAR

K8s(Kubernetes):
  Pod        :最小部署单位(一个或多个容器共享网络和存储)
  Deployment :管理 Pod 的副本数、滚动更新、回滚
  Service    :给 Pod 提供稳定 IP 和 DNS(Pod IP 会变,Service IP 不变)
  Ingress    :外部流量入口,HTTP 路由规则
  ConfigMap  :非敏感的配置数据
  Secret     :敏感数据(密码、Token)
  HPA        :Horizontal Pod Autoscaler → 根据 CPU/内存自动扩缩 Pod

  CI/CD 流程:
    代码提交 → Jenkins/GitLab CI → docker build + push → kubectl apply
    → K8s 滚动更新 → 健康检查 → 流量切换

面试题精选(10 道)

Q1. CAP 理论?为什么不能同时满足?(阿里/腾讯/字节 高频)

标准回答

CAP = 一致性(所有节点同时看同一数据)+ 可用性(每个请求都能正常响应)+ 分区容错(网络故障时系统继续工作)。分布式系统中 P 不可避免(网络可能出问题),必须在 C 和 A 之间取舍。选 CP(如 ZK):Leader 宕机时暂停服务保证一致性。选 AP(如 Eureka):网络分区时所有节点能服务但数据可能不一致。实际系统不是二选一,而是不同程度的取舍(如金融侧重 CP,社交侧重 AP)。

Q2. 分布式事务怎么实现?各自的优缺点?(阿里/美团 高频)

标准回答

四种方案:① 2PC/XA——同步阻塞、协调者单点,传统数据库支持;② TCC——Try-Confirm-Cancel 三段式,业务层实现,灵活但代码侵入强;③ Seata-AT——自动生成 undo_log,无业务侵入但有性能损耗;④ MQ 最终一致性(最常用)——本地事务+消息表/事务消息保证原子发送,高性能、解耦,但非实时一致。

选型:对一致性要求极高(金融)→ TCC;不想改代码 → Seata;大多数场景 → MQ 最终一致性。

Q3. Kafka 为什么高吞吐?(字节/腾讯/快手 高频)

标准回答

五个原因:① 顺序写磁盘(追加写,接近内存速度);② Page Cache(数据先写页缓存,OS 决定刷盘);③ 零拷贝(sendfile,数据从 Page Cache 直发网卡,不经过用户态);④ 分区并行(多 Partition 独立读写,水平扩展);⑤ 批量处理(生产者攒批发送,消费者一次拉取多条,减少网络开销)。

Q4. RabbitMQ 怎么保证消息不丢失?(字节/美团)

标准回答

三端保障:① 生产端——Publisher Confirm(Broker 确认收到,失败重发)+ 持久化(Queue + Message 都持久化);② Broker 端——持久化到磁盘 + 镜像队列(Mirror Queue)防止节点故障;③ 消费端——手动 ACK(处理完确认,未 ACK 重投)+ 死信队列兜底(异常消息不丢失,人工处理)。三端全链路保障,"发-存-收"各环节都有确认机制。

Q5. 缓存和数据库一致性怎么保证?(字节/阿里 超高频)

标准回答

无法保证绝对一致(CAP),只能保证最终一致。最常用方案:Cache-Aside Pattern(旁路缓存)——先更新 DB,再删除缓存(不是更新缓存!)。更新缓存会有并发写问题,删除缓存+延迟双删更安全。更严格场景用:① 订阅 MySQL Binlog(Canal)→ 异步更新/删除缓存;② 分布式事务(MQ 最终一致性);③ 写时直接写 DB,读时永远查 DB + 缓存空结果。

Q6. 分布式 ID 的雪花算法原理?时钟回拨怎么解决?(美团/字节)

标准回答

结构:1bit(不用) + 41bit(毫秒时间戳) + 10bit(机器ID) + 12bit(序列号)。性能极高、趋势递增利于 MySQL 索引。时钟回拨解决:① 短时间回拨(<5ms)→ 自旋等待时钟追上;② 长时间回拨 → 用备用机器 ID 生成;③ 记录"历史最大时间戳",回拨期间使用未来时间戳(但可能产生不连续的 ID)。美团 Leaf 结合号段模式(从数据库批量取 ID 段)作为兜底。

Q7. 服务雪崩怎么解决?(字节/阿里)

标准回答

三层防护:① 限流(Sentinel/Guava RateLimiter,超过阈值直接拒绝或排队,保护自己不被冲垮);② 熔断(错误率或慢调用超过阈值 → 打开熔断器 → 快速失败 → 一段时间后探测 → 半开 → 恢复正常 → 关闭);③ 降级(系统负载高时返回默认值或静态页面,保证核心功能可用)。三层配合 = 防(限流)+ 断(熔断)+ 保底(降级)。

Q8. Nacos 注册中心的原理?和 Eureka 有什么区别?(阿里)

标准回答

Nacos = 注册中心 + 配置中心。注册中心:服务启动时向 Nacos 注册(IP+Port+元数据),定时心跳(5s),Nacos 检测 15s 无心跳标记不健康,30s 剔除。订阅者(客户端)定时拉取服务列表 + Nacos Push 更新通知。

vs Eureka:Eureka 是 AP(自我保护模式,宁可保留过期实例也不剔除),Nacos 支持 CP 和 AP 切换。Eureka 只做注册发现,Nacos 还做配置中心。Eureka 2.0 已闭源,Nacos 是当前主流。

Q9. Gateway 网关的作用?(腾讯/字节)

标准回答

统一入口:路由转发(URL→微服务)、鉴权(认证+授权)、限流、日志、跨域处理、负载均衡。Spring Cloud Gateway 基于 Netty+WebFlux(非阻塞异步),性能比 Zuul 1.x(阻塞)好很多。与 Nginx 的区别:Nginx 在服务最外层(流量入口),Gateway 在微服务层做业务路由。

Q10. K8s 的 Deployment 和 Service 的区别?(字节/阿里)

标准回答

Deployment 管理 Pod(副本数、滚动更新、回滚、启停),声明式控制(定义期望状态,Controller 调节到期望)。Service 给 Pod 提供稳定的网络访问(Pod IP 会变,Service 有稳定的 ClusterIP 和 DNS 名),通过 Label Selector 关联 Pod,默认轮询负载均衡。一句话:Deployment 管"运行",Service 管"访问"。


📊 今日知识图谱

分布式 + MQ + 微服务 DAY 13
│
├── 分布式理论
│   ├── CAP:C(一致) vs A(可用),P 不可避免
│   ├── BASE:基本可用+软状态+最终一致(AP的延伸)
│   ├── Raft:Leader选举(随机超时)+日志复制(多数确认)
│   └── 雪花算法:时间戳+机器ID+序列号,时钟回拨→等待/备用
│
├── 分布式锁 & 事务
│   ├── Redis锁(SETNX+Lua+看门狗,AP) vs ZK锁(临时顺序节点+Watch,CP)
│   ├── 事务:2PC(同步阻塞) → TCC(Try/Confirm/Cancel) → Seata(undo_log) → MQ最终一致
│   └── 本地消息表(同事务写消息)+RocketMQ事务消息(Half→Commit/Rollback)
│
├── 消息队列
│   ├── RabbitMQ:Direct/Topic/Fanout + 生产确认/消费ACK/持久化 + DLX+TTL延迟
│   ├── Kafka:顺序写+PageCache+零拷贝+分区并行+批量=高吞吐
│   │   └── acks=all+手动提交+幂等+事务 保证可靠性
│   └── 选型:RabbitMQ(业务消息,低延迟) vs Kafka(大数据,高吞吐,可回溯)
│
└── 微服务
    ├── Nacos(注册中心+配置中心) + Gateway(路由/过滤/鉴权)
    ├── Sentinel(限流/熔断/降级) + SkyWalking(TraceID/链路追踪)
    ├── 灰度发布:Header染色 → 小比例路由V2 → 观察 → 扩大
    └── K8s:Deployment(副本/更新) + Service(稳定IP) + HPA(自动扩缩)

🔜 明日预告

Day 14 — 场景题 + 算法冲刺 + 项目深挖(Java 方向收官!)

  • 场景题 4 大经典:秒杀系统 / 高可用支付 / 数据迁移 / 扫码登录
  • LeetCode Hot 100 必刷 30 题(Java 版)
  • 项目深挖:STAR 法则 + Java 项目常见追问
  • Java 方向 7 天知识图谱总复习

💡 速通心法:分布式面试的核心是"trade-off 思维"——没有完美方案,只有合适的取舍。CAP 选 C 还是 A?分布式事务选强一致还是最终一致?Redis 锁 vs ZK 锁选性能还是一致性?每次选型都能讲清楚"因为什么场景所以选什么方案,牺牲了什么换来了什么",面试官就会觉得你真的懂分布式。

更多推荐