这次我们直接聊一个面试和架构评审里都绕不开的问题:分布式和微服务到底有什么区别?
很多人在简历上写了“精通微服务架构,具备分布式系统开发经验”,但被问住最多的地方恰恰是这两个概念本身。更常见的情况是,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 节的配置,把注册、发现、事务、锁链完整验证一遍。这一套跑下来,分布式和微服务的区别就不是概念问题了。