这次我们直接聊一个面试和架构评审里都绕不开的问题:分布式和微服务到底有什么区别?

很多人在简历上写了“精通微服务架构,具备分布式系统开发经验”,但被问住最多的地方恰恰是这两个概念本身。更常见的情况是,Spring Cloud、Nacos、Seata 这些组件都在用,真要让讲讲“你现在的系统是分布式还是微服务”,反而说不清楚。这篇文章不绕概念,直接给结论:

微服务是架构风格,分布式是部署形态。两者经常组合出现,但本质上是两回事。文章会从定义、拆分维度、典型中间件、落地路线和踩坑排查几个角度展开,最后给出一套可以对照自己项目做架构判断的清单。

1. 核心概念速览

先建一张表,把两者的关系限定清楚,后面所有内容都围绕这张表展开:

维度 分布式 微服务
本质 系统部署与协作方式 服务拆分与组织方式
关注点 多节点如何对外提供统一能力 业务边界如何拆分、每个服务如何自治
是否必须拆业务 不一定,可以按数据分片、按功能模块复制部署 必须按业务域拆成独立服务
是否必须多节点 是,至少两个节点协同 不绝对,单体也可以叫微服务风格,但实践通常对应多节点
典型例子 Hadoop 伪分布式、Redis Cluster、MySQL 主从 Spring Cloud 微服务、若依微服务、订单/库存拆分
核心问题 网络通信、数据一致性、分布式事务、分布式锁、分布式缓存 服务治理、熔断降级、配置管理、服务发现、链路追踪
常见组件 Zookeeper、Redis、Kafka、Seata Spring Cloud、Nacos、Gateway、Sentinel、SkyWalking

结论先放在这里: 分布式更偏向“多节点协同”的底层能力问题,微服务更偏向“业务如何拆”的架构设计问题 。微服务天生是分布式的,但分布式不等于微服务。

2. 分布式:多节点协同,对外像一个整体

2.1 分布式的核心定义

分布式系统是指多个独立计算机节点通过网络通信协作,共同完成任务,并且对用户来说表现为一个统一的系统。

关键点有三个:

  • 多个节点:至少两台机器,或者一台机器上多个进程。
  • 网络通信:节点之间需要交换数据,不能各干各的。
  • 对外统一:用户访问的是同一个服务入口,不需要关心背后有几个节点。

Hadoop 伪分布式就是一个很好的入门例子。所谓伪分布式,是在单台机器上模拟分布式环境,HDFS 的 NameNode、DataNode、SecondaryNameNode 以独立进程的方式运行。这说明分布式的本质是“多进程 + 通信 + 协作”,而不是必须有物理上的多台服务器。

2.2 分布式要解决什么问题

节点一多,新问题就出来了:

  • 数据一致性:两个节点同时改同一份数据怎么办。
  • 分布式事务:跨库、跨服务的数据操作如何保证原子性。
  • 分布式锁:多节点抢同一个资源,如何保证只有一个节点能拿到。
  • 分布式缓存:缓存数据分散在多个节点,如何保证访问路由和数据一致。
  • 分布式定时任务:定时任务在多个节点部署后,如何避免重复执行。
  • 分布式存储:数据量超过单机容量,如何分片存储。

这组问题不依赖具体业务,只要系统有多个节点协作,就一定会遇到。

2.3 分布式事务的四种典型方案

这里必须提分布式事务,因为它是分布式系统里最容易出问题、面试也最爱问的一块。常见方案是这四种:

方案 核心思路 典型组件 适用场景
2PC/XA 两阶段提交,先准备后提交 Atomikos、Seata AT 强一致性、短事务
TCC Try-Confirm-Cancel 三阶段 Seata TCC 业务可拆分为预留/确认/撤销,比如扣款
最大努力通知 允许最终不一致,多次重试通知 MQ + 定时任务 跨平台回调、支付结果通知
本地消息表 + MQ 事务消息实现最终一致性 RocketMQ、RabbitMQ 订单、库存类异步削峰场景

Seata 是目前 Java 生态里用得非常多的分布式事务框架,AT 模式依赖全局锁和undo_log 表回滚。使用时要特别注意代理数据源配置,不能用默认的 DataSource,否则事务回滚不生效。

下面是一段 Seata AT 模式的 YAML 配置示例,结合 Spring Cloud 项目使用:

seata:
  enabled: true
  application-id: order-service
  tx-service-group: my_test_tx_group
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: ""
      group: SEATA_GROUP
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      namespace: ""
      group: SEATA_GROUP

对应 Spring Boot 主类上需要启用全局事务:

@SpringBootApplication
@EnableAutoDataSourceProxy
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

注意:Seata AT 模式需要业务表包含 undo_log 表,且每个分支事务都要走 Seata 代理的数据源,否则回滚时找不到原始数据。

3. 微服务:业务边界拆分,服务独立演进

3.1 微服务的核心定义

微服务是一种架构风格,将单个应用拆分成一组小型服务,每个服务围绕业务能力构建,独立开发、独立部署、独立扩展,服务之间通过轻量级通信机制(HTTP/RPC)交互。

微服务强调的是“边界”和“自治”。

  • 边界:按业务域划分,比如订单服务、库存服务、用户服务。
  • 自治:每个服务有自己的数据库、自己的代码仓库、自己的发布节奏。

3.2 微服务不是简单的“拆”

很多人拿到一个单体应用,直接复制一份再改改端口,以为这就是微服务。这其实是“分布式部署的单体应用”,不是微服务。

判断一个系统是不是微服务,可以看三个问题:

判断问题 是微服务的话
每个服务的数据是否独立? 是,不允许跨库直接 join
某个服务可以独立部署吗? 可以,发布不影响其他服务
服务团队是否需要和其他团队频繁协调? 不需要,接口定义好就行

如果答案是“数据还共用一个库”“发布必须一起发”,那充其量是伪微服务,或者叫分布式的单体。

3.3 微服务落地的典型组件

以 Java 技术栈为例,一个完整的微服务架构需要这些能力:

能力 典型组件 作用
服务注册与发现 Nacos、Eureka、Consul 服务实例动态注册和发现
网关 Spring Cloud Gateway 统一入口、路由、鉴权
负载均衡 OpenFeign + LoadBalancer 服务间调用和负载均衡
配置中心 Nacos Config、Apollo 集中管理配置,支持动态刷新
熔断降级 Sentinel、Resilience4j 保护依赖不健康的服务
链路追踪 SkyWalking、Zipkin 全链路日志追踪和性能分析
分布式事务 Seata 跨服务事务一致性

用 Nacos 做注册中心时,服务配置示例:

spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: public

服务启动后,可以在 Nacos 控制台看到实例列表。调用方通过 OpenFeign 声明式调用:

@FeignClient(name = "user-service", path = "/api/user")
public interface UserClient {
    @GetMapping("/info")
    UserInfo getInfo(@RequestParam("id") Long id);
}

这套结构是当前大部分 Java 微服务项目的通用骨架,若依微服务版本也是基于 Spring Cloud + Nacos 搭建的。

4. 分布式和微服务的核心区别

把两者放在一起对比,核心区别可以归结为四个层次。

4.1 拆分维度不同

维度 分布式 微服务
按什么拆 按部署单元、数据分片、功能复制 按业务能力边界
拆分目标 支撑更多请求、更大数据量 提升研发效率、降低耦合、独立演进
最小拆分单位 进程/节点 独立业务服务

4.2 关注问题不同

分布式关注的是多节点带来的技术问题:数据一致性、网络分区、节点故障、时钟漂移。

微服务关注的是架构管控问题:服务怎么拆、接口怎么定义、依赖怎么管理、故障怎么隔离。

4.3 是否共享数据

这一点是很多人的盲区。

分布式系统可以共享底层数据,比如 Redis Cluster 每个节点负责一部分 slot,MySQL 主从共享同一份逻辑数据。微服务则明确要求数据独立,服务之间只能通过接口访问数据,不能直接共享数据库表。

4.4 一句话总结

  • 分布式解决的是“多台机器如何一起干活”的问题。
  • 微服务解决的是“一个复杂系统如何拆成小块维护”的问题。
  • 分布式是基础设施层面的特征,微服务是应用架构层面的选择。
  • 微服务系统一定是分布式的,但分布式系统可以是单体应用的集群部署。

5. 从单体到分布式,再到微服务

5.1 单体阶段

所有功能在一个应用里,部署一个 jar/war,共用一个数据库。

优点:开发简单、部署简单、事务天然一致。 缺点:代码膨胀后无法维护,任何一个功能发布都要整体回归。

5.2 集群/分布式阶段

单体应用复制多份部署到多台机器,前面加负载均衡。这是“单体的分布式”。

解决了高可用和高并发,但没有解决代码耦合问题。这时候会遇到 Redis 分布式锁、分布式定时任务不重复执行等问题。Redisson 就是常用来解决分布式锁的框架:

RLock lock = redissonClient.getLock("order:create:123");
boolean locked = lock.tryLock(3, 30, TimeUnit.SECONDS);
if (locked) {
    try {
        // 执行订单创建逻辑
    } finally {
        lock.unlock();
    }
}

5.3 微服务阶段

按业务能力拆分成多个服务,每个服务独立部署、独立数据库。服务之间通过 OpenFeign、Dubbo 或者 HTTP 调用。

这时候需要引入服务注册中心、网关、配置中心、熔断降级和链路追踪。

5.4 三者关系演化表

阶段 实例数量 数据存储 业务耦合 典型组件
单体 1 单库 Spring Boot
集群分布式 N 单库或主从 Nginx、Redis
微服务 N 按服务拆分 Spring Cloud + Nacos + Seata

6. 分布式带来的典型问题:拆完之后更难了

这是写代码之外最需要理解的部分。架构迁移到分布式或微服务后,原本单体里简单的问题会变得复杂。

6.1 分布式事务

单体时代一个 @Transactional 就能保证订单和库存同时更新。微服务拆分后,订单服务和库存服务各自有库,事务边界越界了。

解决方案在 2.3 节已经列过,实际项目建议按一致性要求选型。无法接受最终一致性、要求强一致的场景,用 Seata AT 或 TCC。允许延迟一致、异步链路,用事务消息和本地消息表,比如订单创建成功后发送“库存扣减”消息。

6.2 分布式锁

在集群部署下,传统的 synchronized 只能锁住单个 JVM 内的线程,锁不住多节点。

常见做法是 Redis 分布式锁,使用 Redisson 封装好的 RLock,避免自己写 setnx 出现释放锁错误。用 Zookeeper 临时顺序节点也可以实现分布式锁,适合对一致性要求更高的场景。需要特别注意的是锁粒度、锁超时、可重入性,以及业务执行时间超过锁过期时间导致的提前解锁问题。

6.3 分布式缓存

缓存从单机本地缓存变成 Redis Cluster 后,要解决缓存穿透、缓存击穿、缓存雪崩、缓存一致性问题。面试老四样,但在分布式环境下才真正变得严重。

6.4 分布式定时任务

定时任务在单机部署没问题,多节点部署后同一个任务会被重复执行。方案有三类:

方案 实现方式 适用场景
分布式锁 任务执行前抢锁 简单,任务较少
Quartz 集群模式 基于数据库锁 中小型集群
XXL-Job 独立调度中心 + 分片广播 大规模、需要管理界面

6.5 分布式压测

这也是搜索结果里经常出现的词。JMeter 支持分布式压测,master 节点分发脚本,多台 slave 节点同时发起请求。目的不是让压力更大,而是突破单机端口和连接数限制,模拟更真实的流量。

JMeter 分布式压测的格式:

# 在 slave 节点启动服务
jmeter-server -Dserver.rmi.port=1099

# 在 master 节点执行远程测试
jmeter -n -t test.jmx -R 192.168.1.101:1099,192.168.1.102:1099 -l result.jtl

注意:master 和 slave 的 JDK 版本最好一致,否则 RMI 通信会报错;测试脚本中用到的 CSV 数据文件要手工同步到每个 slave 节点,因为 JMeter 不会自动分发文件。

7. 架构选型:该用分布式还是微服务

这是文章最实用的一节。很多开发者在“要不要微服务化”上反复纠结,提供一套判断标准。

7.1 什么时候用分布式

  • 单体应用遇到单机性能瓶颈,需要横向扩容。
  • 需要高可用,一台机器挂了服务不能断。
  • 数据量超过单库存储能力,需要分库分表或引入分布式存储。
  • 需要多节点协作,但业务不需要拆成独立服务。

这时候选择“单体应用多实例部署”,加 Nginx 做负载均衡,再引入 Redis 做分布式缓存,就已经是分布式系统了。不需要上微服务。

7.2 什么时候需要微服务

  • 单体代码量快速膨胀,模块之间耦合严重,想拆拆不动。
  • 团队规模扩大,多个团队需要在同一个系统并行开发。
  • 某个模块的流量远高于其他模块,需要独立扩展。
  • 需要不同的技术栈实现不同业务域。
  • 业务变更频繁,希望隔离故障,单个模块出错不影响全局。

7.3 什么时候不要上微服务

  • 团队只有几个人,业务逻辑简单。
  • 没有独立运维能力,没有监控、日志、链路追踪。
  • 项目还是原型验证阶段,业务边界本身不清晰。
  • 事务一致性要求极高,又不想承担分布式事务的技术成本。

**不建议为了简历写“微服务经验”硬拆系统。**微服务是手段,不是目的。拆完之后引入的注册中心、网关、配置中心、分布式事务、链路追踪,每一样都是实实在在的运维负担。

8. 常见问题与排查方法

从搜索结果看,分布式和微服务相关的坑集中在事务、锁、服务注册和配置管理上。

问题现象 可能原因 排查方式 解决方案
服务启动后 Nacos 看不到实例 服务未注册成功、namespace 不一致、网络不通 检查 nacos 控制台、查看服务日志、确认 namespace 修正 namespace 配置,重启服务
Feign 调用报 “No instances available” 目标服务未注册或注册失效 检查目标服务状态、Nacos 健康检查配置 重启失效服务,延长心跳超时时间
Seata 事务不回滚 未使用代理数据源、不会生成 undo_log 查看分支事务日志、检查 undo_log 表 启用 @EnableAutoDataSourceProxy ,补建 undo_log 表
Redis 分布式锁提前失效 业务执行时间超过锁超时时间 加日志记录加锁时间和业务耗时 延长过期时间,或使用看门狗续期机制
分布式定时任务重复执行 多节点无互斥机制 观察多个实例日志是否同一时刻执行 引入 XXL-Job 或加分布式锁
JMeter 分布式压测连接失败 RMI 端口未开、JDK 版本不一致 在 slave 机器 telnet 测试端口 统一 JDK 版本,开放 1099 端口
配置修改后服务未生效 Nacos Config 未配置自动刷新 检查 @RefreshScope 注解 加注解,或手动调用刷新接口
微服务调用超时 网络延迟、下游慢、连接池耗尽 查 SkyWalking、调整 Feign 超时时间 设置合理的超时和重试策略

9. 最佳实践与使用建议

结合真实项目经验,给出几条工程化建议。

9.1 先小参数验证,再大规模拆分

不要一次性把整个系统拆成几十个服务。先选一个业务边界清晰、相对独立的模块试点,比如用户服务或者日志服务。跑通注册中心、网关、配置中心、日志链路之后,再逐步扩大拆分范围。

9.2 保留一套最小可运行配置

把 Nacos、Seata、Redis、Sentinel 的最小配置模板存到代码仓库里。新服务拉下来改端口和数据库就能跑。不要每次新服务都重新配置一遍中间件。最小可运行配置至少包括:

spring:
  application:
    name: ${service.name}
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: public
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/${db.name}?useUnicode=true&characterEncoding=utf8
    username: root
    password: root
seata:
  enabled: true
  application-id: ${service.name}

9.3 目录和命名规范

模型文件、输入素材、输出结果分目录管理。放到项目里对应的是:代码、配置、日志、数据要分离。配置放到 Nacos 统一管理,日志按 traceId 落盘,输出结果统一走接口。服务命名用“业务域-子模块”的格式,例如 order-service、stock-service。

9.4 批量任务要加日志和失败重试

分布式环境里批量任务一旦失败,排查成本很高。每个任务要有任务 ID、执行日志、失败重试机制。推荐结合 MQ 做失败重试,或者用 XXL-Job 的失败告警。

9.5 接口服务和数据访问要限制范围

将服务注册到 Nacos 后,接口不能随意暴露到公网。网关层做鉴权,内网服务之间通过 service name 调用,不直接暴露 IP。数据库账号按服务隔离,单个服务只能访问自己的 schema。

9.6 发布或商用前要做效果复核

微服务化之后,每次发布都要做接口回归和压测。建议在测试环境完整跑一遍核心链路,特别注意分布式事务和缓存一致性在真实请求量下是否稳定。

10. 总结与下一步

最值得记住的一句话是: 分布式是部署形态,微服务是架构风格。

  • 如果面试官问区别,从拆分维度、关注问题、数据共享三个角度回答,能直接讲完核心。
  • 如果自己在做架构选型,先问一个问题:现在系统是单机遇到瓶颈,还是单体代码耦合到无法维护?前者考虑分布式集群,后者才需要认真思考微服务拆分。
  • 最容易踩的坑是:把单体复制多份,起了一个分布式集群,就对外宣称是微服务。代码层面没有边界拆分、数据层面没有独立、治理层面没有服务发现,这就不是微服务。
  • 分布式事务和分布式锁,是上手分布式后最先会碰到的技术难点。Seata 和 Redis/Redisson 是最快见效的组合。
  • 后续可以继续扩展的方向:链路追踪(SkyWalking)、分布式定时任务(XXL-Job)、JMeter 分布式压测、服务网格(Istio)。

建议先把 Nacos + Seata + Redisson 这三件套在一个 Spring Cloud 项目里跑通,对照本文第 2、3 节的配置,把注册、发现、事务、锁链完整验证一遍。这一套跑下来,分布式和微服务的区别就不是概念问题了。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐