首页 / 知识库 / python服务端进阶 / 后端微服务架构

05 - 数据怎么办:每个服务一个数据库

这一章讲微服务里最反直觉、也最容易踩坑的一条原则:

每个服务,管自己的数据库。别人不许直接来查。

先看一个”看起来很方便”的错误做法

拆成微服务后,很多人第一反应是:服务归服务,但数据库还是共用一个吧,这样订单服务想要用户数据,直接连过去查一下多方便。

   ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐
   │用户服务 │  │商品服务 │  │订单服务 │  │支付服务 │
   └───┬────┘  └───┬────┘  └───┬────┘  └───┬────┘
       └───────────┴─────┬─────┴───────────┘

              ┌────────────────────┐
              │  一个共享的大数据库    │  ← 谁都能直接读写所有表
              └────────────────────┘

方便是方便,但这等于又把大家焊回去了——正是我们拆微服务想逃离的东西。

为什么共享数据库是个陷阱

陷阱 1:又变回”牵一发动全身”

订单服务嫌 users 表的某个字段不好用,想改表结构。可用户服务、支付服务也在读这张表……一改,全崩。数据库又变成了那个”谁都不敢动”的焊死点。

陷阱 2:谁也不知道数据被谁动了

四个服务都能写 orders 表,出了脏数据,根本查不出是哪个服务干的。责任边界一片模糊。

陷阱 3:没法各自扩容、各自选型

订单数据量暴涨想单独优化?想给商品用别的数据库?——共用一个库,动不了。

正确做法:每服务一库

   ┌────────┐    ┌────────┐    ┌────────┐    ┌────────┐
   │用户服务 │    │商品服务 │    │订单服务 │    │支付服务 │
   └───┬────┘    └───┬────┘    └───┬────┘    └───┬────┘
       ▼             ▼             ▼             ▼
   ┌───────┐     ┌───────┐     ┌───────┐     ┌───────┐
   │用户库  │     │商品库  │     │订单库  │     │支付库  │
   └───────┘     └───────┘     └───────┘     └───────┘

  规矩:想要别人的数据?不能直接连它的库,
        必须走它的接口去"问"(回到第 04 章的通信方式)。

这样每个服务的数据是私有的,别人只能通过它对外的接口来拿。它内部想怎么改表、换什么数据库,都不影响别人。数据也解耦了。

但新问题来了:跨服务的一致性

这是微服务最头疼的问题。看”下单”这个动作,它其实要动两个服务的数据:

  • 订单库:创建一条订单
  • 商品库:把库存减 1

在单体里,这俩操作能放进一个数据库事务,要么全成功、要么全失败(你在 PostgreSQL 里学过的事务)。可现在它们在两个库里,一个事务包不住了。万一:

订单创建成功了 ✅,但扣库存请求失败了 ❌ —— 用户下了单,库存却没减,超卖了。

应对思路(只讲思想,不抠实现)

微服务里,我们放弃了”任何时刻数据都严丝合缝”的执念,换成一个更现实的目标:

最终一致性:允许中间有极短暂的不一致,但通过一些机制,保证最后数据一定会对上。

一个常见的落地思路,正好用上你学过的消息队列:

1. 订单服务:先把订单创建了(自己库里的事,稳)
2. 订单服务:往消息队列丢一条"订单已创建,请扣库存"
3. 商品服务:从队列取到消息,去扣库存
      ├─ 扣成功 → 完事
      └─ 扣失败 → 消息重试,或发一条"扣减失败"事件
                  触发订单服务把这单取消(补偿)

核心思想两句话:

  • 用异步消息把多个服务的动作串起来,别指望一个大事务锁住全部;
  • 万一中途失败,就用”补偿动作”把已经做的那步撤销(比如把订单标记为取消)。

这类”用一连串带补偿的步骤,来完成一个跨服务操作”的模式,业界叫 Saga。名字知道即可,思想才是关键——接受短暂不一致,靠消息和补偿达成最终一致

小结

  • 微服务铁律:每个服务管自己的库,别人只能通过接口来要数据,不能直连别人的库。
  • 共享数据库看着方便,实则把服务又焊死,是个陷阱。
  • 代价是跨服务一致性变难:多个库没法用一个事务包住。
  • 应对思想:追求最终一致性,用消息队列串联 + 失败时补偿,而不是强求时时刻刻严丝合缝。

上一章 ← 04 - 服务之间怎么”说话” | 下一章 → 06 - 一套微服务系统的全景架构 | 回到 README 目录