服务拆分
如何进行微服务的在设计之初的拆分
首先,微服务拆分之所以能显著提升企业需求响应速度,本质是通过架构解耦实现研发效能的非线性增长。
一般是从业务领域出发,按边界领域进行拆分,微服务的本质是 “业务服务化”,因此最核心的拆分依据是业务领域的自然边界。可以通过 “领域驱动设计(DDD)” 梳理业务模块,识别出独立的 “领域”,每个领域对应一个或多个微服务。
- 梳理业务流程:列出系统的核心业务流程(如电商的 “商品管理”“订单处理”“支付结算”“用户会员” 等)。
- 划分领域边界:每个业务流程中,识别出 “不可再分” 的独立业务单元(领域),这些单元应具备完整的业务能力,且与其他单元的依赖较弱。
- 定义服务职责:每个领域对应一个服务,明确其核心职责(如 “订单服务” 负责订单的创建、修改、查询、取消等全生命周期管理)。
数据是服务的 “血液”,如果两个服务的核心数据紧密关联(如同一笔业务的核心数据分散在两个服务),会导致频繁的跨服务数据交互,增加耦合。因此,拆分时需确保每个服务拥有独立的核心数据,即 “数据自治”。
- 每个服务应包含自身业务的核心数据库表,且这些表不应被其他服务直接访问(需通过 API 调用获取数据)。
- 若两个业务的核心数据无法分离(如 “用户” 和 “用户地址”),可暂时放在同一服务,避免为了拆分而拆分。
所以说,微服务在拆分的时候要注意如下内容:
- 不同的微服务,他们的业务领域是独立的,不要重复开发相同的业务
- 微服务数据独立,不要访问其他微服务的数据库
- 微服务可以将自己的业务暴露为接口,供其他微服务调用
微服务拆分的方案简介
微服务架构中第一件事应该就是拆分服务,微服务架构中的业务,对于服务范围的定义应该以发展的眼光去看待。随着系统的发展,可能会出现微服务的拆分和合并。这就像面向对象设计的单一职责原则,每个人对单一职责的认知各不相同,而且随着系统的发展,原来符合单一职责的对象可能会变得复杂,直到某一天被重构。
微服务也是同样的道理。某个服务中的一块业务,在设计之初可能非常简单,常常依附于其他业务,数据也和主服务存在于同一个数据库中。随着业务增长,这个模块的功能可能越来越复杂,直到和其他业务也能平起平坐,和其他业务的耦合度越来越高,此时就需要拆分为独立的微服务。
拆分不仅是 “代码层面拆成独立服务”,更要考虑 “数据层面的独立”,因为数据是服务独立的基础。具体包括:
- 服务拆分:将原业务模块的代码、接口、配置等独立出来,形成新的服务(有自己的部署单元、调用入口)。
- 数据层拆分:包括数据库(DB)、消息队列(如原服务共用的 RabbitMQ,新服务可能需要独立队列)、缓存(如 Redis 实例拆分)、云上存储(如对象存储的 Bucket 拆分)等,核心是让新服务的数据不再依赖原服务的存储。
拆分的核心原则是 “稳妥”—— 避免一次性拆分导致线上故障,因此分三步上线:
- 只拆服务,不拆 DB(服务独立,数据仍共享)
- 操作:新服务的代码独立部署,对外提供接口,但读写的数据仍和原服务共用同一个数据库(甚至同一个表)。
- 目的:先验证 “服务独立运行” 的可行性,比如新服务的接口调用、依赖关系、性能是否正常,同时避免数据层变动带来的风险。
- 例子:拆分 “评价服务”
时,先让它独立部署,但评价数据仍存在原 “订单库”
的
comment
表中,新服务直接读写该表。 - 风险控制:此时数据还是 “共用”,如果新服务有 bug,可快速回滚到原服务调用逻辑,影响范围小。
- 拆分 DB Schema(数据结构独立,仍在同一数据库实例)
操作:在同一个数据库实例中,为新服务创建独立的 Schema(或独立的表),将原表中的数据同步到新表,新服务改为读写新表;原服务仍读写旧表,通过同步机制(如触发器、定时任务)保持新旧表数据一致。
目的:验证 “数据结构独立” 的可行性,确保新服务对新表的读写逻辑正确,同时通过数据同步避免数据不一致。
例子:在原订单库中新建
review_schema
,创建review
表,将comment
表的数据同步过来,评价服务改为读写review
表,同时通过脚本让comment
表和review
表的数据实时同步。关键:这一步是为了让新服务适应 “自己的数据表”,同时确保原服务不受影响(仍用旧表),为最终拆库做准备。
- 拆分 DB(数据库实例完全独立)
- 操作:将新服务的 Schema 和表迁移到独立的数据库实例中(新的 DB 服务器或云数据库实例),停止原数据同步机制,新服务完全依赖自己的数据库,原服务也不再与新服务有任何数据关联。
- 目的:实现数据层的彻底独立,避免原服务的数据库压力影响新服务,也让新服务的数据扩容、备份等操作更灵活。
- 例子:将
review_schema
迁移到新的 MySQL 实例,评价服务的数据源配置指向新实例,删除旧表与新表的同步脚本,完成最终拆分。

如何进行服务拆分
在上面的第一阶段,只拆服务,不拆 DB 的步骤中,拆分服务的工作是将原 A 服务的 API 拆一部分出来,移到到新建的 B 服务中。暂时保留两个版本的 API。这个阶段不拆分 DB,目的是为了先确保 API 拆分的正确性、完整性。一旦出现问题,切换路由,快速回滚,不会污染数据。
其中,涉及到拆分服务的内容,拆分服务的方案需要考虑如下
- 将服务 A 中 B 业务的 API 全部拆分到新的微服务 B 中
- A 服务中暂时保留 B 业务 API,以便出问题时,切换回原有 API
- 日志需要记录统计哪个服务的 API 被调用
- B 服务继续使用原有 DB
在拆分前,首先要明确哪些 API 需要迁移,方法是:从原服务(假设为 A
服务)的数据库中,找出属于待拆分新服务(B 服务)的表(比如 A
服务里的user_reviews
表实际属于 B 服务的
“评价业务”),然后向上追溯 —— 这些表被哪些 API 读写过?这些 API
就是需要处理的迁移范围。
根据 API 的 “所属业务” 和 “访问的数据表归属”,涉及到改动的 API 分类如下
- 第一类:B 业务的 API 只访问 B 业务表(纯 B
业务,无依赖)
- 特点:API 的功能属于 B 业务(比如 “提交评价”
接口),且读写的数据只涉及 B
自己的表(比如
reviews
表),不涉及 A 服务的表。 - 处理方式:直接 “原样迁移”—— 把这些 API 的代码从 A 服务复制到 B 服务,调整配置(如数据库连接指向后续拆分的 B 库)后,由 B 服务独立提供接口。
- 例子:电商系统中,“查询用户评价列表” 的
API,只查
reviews
表(B 的表),直接迁移到 B 服务即可。 - 为什么简单? 因为无跨业务依赖,拆分前后逻辑一致,风险最低。
- 特点:API 的功能属于 B 业务(比如 “提交评价”
接口),且读写的数据只涉及 B
自己的表(比如
- 第二类:A 业务的 API,但要使用 B 业务表(A 依赖 B
的数据)
- 特点:API 的功能属于 A 业务(比如 “展示订单详情”
接口,属于订单服务 A),但需要读取 B 业务的表(比如为了显示
“该订单对应的评价”,需要查
reviews
表)。 - 拆分前:A 服务可以直接读写 B 的表(因为同库或跨库直连)。
- 拆分后:A 服务不能再直接访问 B 的表(数据层独立),必须通过调用 B 服务的 API 获取数据。
- 处理方式:在 B 服务中新增对应的 API,供 A
服务调用。
- 比如 A 的 “订单详情” 接口需要评价数据,B 服务新增 “根据订单 ID 查询评价” 的 API,A 服务调用该 API 获取数据,不再直接查 B 的表。
- 关键:A 服务的功能不变,但数据获取方式从 “直接读表” 改为 “调用 B 的 API”,同时 B 服务需要扩展接口能力。
- 特点:API 的功能属于 A 业务(比如 “展示订单详情”
接口,属于订单服务 A),但需要读取 B 业务的表(比如为了显示
“该订单对应的评价”,需要查
- 第三类:B 业务的 API,但要使用 A 业务表(B 依赖 A
的数据)
- 特点:API 的功能属于 B 业务(比如
“评价时展示商品名称” 接口,属于评价服务 B),但需要读取 A
业务的表(比如
products
表,属于商品服务 A)。 - 拆分前:B 服务可以直接读写 A 的表。
- 拆分后:B 服务不能直接访问 A 的表,必须通过调用 A 服务的 API 获取数据。
- 处理方式
- 先将该 API 从 A 服务迁移到 B 服务(保证 B 业务的接口归属正确);
- 同时在 A 服务中新增对应的 API,供 B 服务调用获取 A 的数据。
- 例子:B 的 “提交评价” 接口需要验证商品是否存在(读 A 的 products
表),拆分后:
- “提交评价” 接口迁移到 B 服务;
- A 服务新增 “根据商品 ID 查询商品是否存在” 的 API,B 服务调用该 API 验证商品,不再直接查 A 的表。
- 特点:API 的功能属于 B 业务(比如
“评价时展示商品名称” 接口,属于评价服务 B),但需要读取 A
业务的表(比如

这样分类的目的其实跟微服务的原则密切相关,微服务拆分的核心是 “服务与数据独立”,但业务间的依赖关系不会消失(比如订单依赖评价、评价依赖商品)。这三类划分的目的是:
- 明确 API 的归属(保证业务边界清晰);
- 解决 “跨服务数据访问” 的问题(用 API 调用替代直接表访问,符合微服务的通信规范);
- 分场景制定迁移策略,避免拆分后出现数据访问失败或业务逻辑断裂。
通过这种分类,能确保拆分后服务间的依赖通过 “API 调用” 而非 “直接读写表” 实现,既保持业务连续性,又符合微服务 “松耦合” 的设计原则。
进行数据库的拆分
在完成 API 拆分并确认所有 B 业务的 API 调用都由 B 服务处理后,我们还不能直接进行数据库的拆分,因为存在一些潜在的问题尚未验证。比如,我们无法确定 A 服务是否已经彻底移除了对 B 业务表的所有访问,可能还有遗漏的地方;同时,数据库权限的设置也没有在生产环境中得到实际验证。如果在这种情况下直接拆分 DB,一旦出现上述问题,程序就会出错,数据可能被污染,而且恢复起来难度很大。
假设我们要从 “订单服务(A 服务)” 中拆分出 “评价服务(B
服务)”,订单服务的数据库(A
DB)里原本有orders
(订单表)和reviews
(评价表)。当
API
拆分完成后,评价服务的接口已独立,但还存在隐患,如果直接把reviews
表移到新数据库,一旦有隐藏的读写操作,就会导致评价数据丢失或错乱(比如用户提交评价后,A
服务的旧代码试图更新reviews
表却失败)。
因此,先在 A DB 内部建一个 B 专属的 Schema(类似
“文件夹”),把reviews
表挪进去,模拟
“数据隔离”,验证没问题后再真拆。
这个阶段的开发工作主要有以下几点:
在原 DB 上建立 B 业务的 Schema,并将 B 业务表迁移到这个 B 业务 schema 中。这一步是为了在数据结构层面将 B 业务的数据与 A 业务的数据进行分离,为后续的实际拆分做准备。
重新建立 A、B 服务的 DB 账号,并为它们重新分配相应的权限。这样可以确保 A 服务只能访问到其对应的业务数据,B 服务也只能访问到自己的业务数据,从权限上进行隔离。
A、B 服务修改 DB 账号和 schema 信息。使得 A 服务和 B 服务能够正确连接到对应的 schema,获取所需的数据。
上线时的工作主要包括:
数据迁移,将 B 业务表的数据迁移到 B Schema 中。确保 B 业务的数据在新的 schema 中能够正常存在和使用。
切换 A、B 服务的 DB 账号。让 A 服务和 B 服务使用新的账号访问对应的 schema,验证权限设置是否正确,以及服务能否正常运行。
在完成 Schema 拆分后,当确认 A Schema 中 B 业务表已经没有数据流入,B schema 的数据流入正常,并且系统运行稳定后,就可以进行 DB 的拆分了。
DB 拆分分为以下几步:
提前建立 B 服务 DB,并从 A 服务 DB 同步数据。先搭建好 B 服务独立的数据库环境,然后将 A 服务 DB 中属于 B 业务的数据同步到新建立的 B 服务 DB 中,保证数据的一致性。
准备好 B 服务所需要的 DB 账号信息。确保 B 服务有合适的账号能够访问新的数据库。
当 A 服务 DB 数据同步完成后,切换 B 服务到新的 DB。让 B 服务正式使用独立的数据库,从此不再依赖 A 服务的 DB。
DB 拆分后,当确认 A DB 中的 B schema 没有数据流入后,就可以进行收尾工作了,主要包括:
移除 A DB 中的 B schema。彻底清除 A DB 中与 B 业务相关的数据结构,避免占用资源和产生混淆。
移除 A DB 中的 B 业务表。进一步清理 A DB 中遗留的 B 业务相关表,确保 A DB 中只保留 A 业务的数据。
其他服务中如果有临时保留的、用于切换回 A 服务原来 API 的代码,要进行删除。保证系统中不再有冗余的、无用的代码,使系统结构更清晰。
移除 A 服务中老版本的 API。因为这些老版本的 API 已经被迁移到 B 服务或者不再使用,移除它们可以避免调用错误,使服务接口更简洁。
微服务拆分与远程服务调用
服务远程调用是微服务拆分后服务间协作的核心方式,其设计与实现直接体现了微服务拆分的特点和挑战。
在单体应用中,模块间通过内存调用(如方法调用)完成协作,而微服务拆分后:
- 每个服务独立部署在不同的进程甚至不同的服务器上,物理隔离使得内存调用不可行。
- 服务间的协作必须通过网络通信实现(即远程调用),例如用户下单流程可能需要调用 “订单服务”“库存服务”“支付服务”,这些调用均为跨服务的远程操作。
所以说,远程调用是微服务拆分后的必然结果,服务解耦之后保持业务连贯性就是通过服务远程调用实现,也就是一个业务模块的部分能向另一个模块的部分发送请求。
微服务拆分后,服务的技术栈可能多样化(如 Java、Go、Python 等),远程调用协议需要适配这种异构性,常见选择包括:
- RESTful API:基于 HTTP/HTTPS,协议简单、跨平台性强,适合大多数服务间的同步通信(如用户服务调用认证服务验证 token)。
- RPC(远程过程调用):如 gRPC(基于 HTTP/2)、Dubbo(基于 TCP),性能更高、接口定义更严格,适合服务间高频、低延迟的通信(如订单服务调用库存服务扣减库存)。
- 消息队列:如 RabbitMQ、Kafka,通过异步消息传递实现解耦,适合非实时性场景(如订单完成后通知物流服务发货)。
总结
从宏观看待类似微服务拆分工作。我总结以下几点
- 分析先行,合理的分析方法,准确确定影响范围
- 分多阶段进行,一次拆分一部分,风险可控、方便回滚
- 新的新建;旧的保留;新的验证;旧的删除
- 每一个阶段都要有风险应对方案
- 每一个阶段都要有验证方案
- 不要忽视对第三方的影响