数据所有权与去共享库
用途:微服务落地时最难的一步——数据边界怎么划、怎么禁止跨服务读表
问题
「问题」小节链接多数“伪微服务”的真实形态是:服务拆了,数据库没拆。多个服务读写同一张表,于是:
- 任何字段变更都是跨团队破坏性变更;
- 业务规则散在各服务的 SQL 里,无法集中演进;
- 无法独立部署与独立扩容(数据库成为共享状态);
- 一致性靠“大家都小心点”维持。
数据所有权原则:每张表只有一个写入方(owner 服务),其他方只能通过 owner 暴露的接口或事件获取数据。
落地形态
「落地形态」小节链接| 需求 | 方案 | 注意点 |
|---|---|---|
| 其他服务需要查询 owner 的数据 | API 查询 / 事件订阅本地副本 | 副本允许延迟,需要版本与失效策略 |
| 跨服务聚合查询(列表页要展示多方字段) | BFF/查询服务并行调用;或建投影/宽表;或同步到搜索引擎 | 避免串行调用链过长;注意部分失败降级 |
| 报表与分析 | 数据仓库/湖,从各服务库抽取 | 不要为了报表开放生产库直连 |
| 强一致跨表事务 | 归入同一 owner 服务,或改用同库不同 schema | 跨服务强一致是反模式信号 |
| 历史数据迁移 | 双写 + 回填 + 校验 + 切读 + 停写 | 每阶段都要有对账与回滚开关 |
禁止跨库访问的工程手段
「禁止跨库访问的工程手段」小节链接- 权限层面:数据库账号按服务隔离,非 owner 无写权限(甚至无读权限)。
- 架构测试:禁止 import 其他模块的持久层类。
- 网关/服务网格:数据库访问走 sidecar 或代理,可审计来源。
- 代码评审清单:新增 SQL 必须回答“这张表的 owner 是谁”。
事件驱动的数据副本
「事件驱动的数据副本」小节链接订阅 owner 的事件在本地维护只读副本,是消除同步依赖的常用做法:
- 副本表结构面向本地查询需求设计,不必与源一致;
- 必须处理乱序与重复(版本号 + 幂等写入);
- 需要初始化路径(快照 + 增量)与重建能力;
- 明确可接受延迟,并在 UI 上体现“数据可能滞后”。
常见误区
「常见误区」小节链接| 误区 | 修正 |
|---|---|
| 拆服务不拆库 | 数据未隔离时,服务拆分只是形式 |
| 共享只读库当“解耦” | 只读也造成 schema 耦合,变更仍会破坏下游 |
| 每个服务各自缓存一份全量数据 | 无失效策略的副本比共享库更危险 |
| 用分布式锁掩盖数据归属问题 | 回到 owner 与事件模型 |
| 迁移期间长期双写无退出计划 | 设定切读与停写时间点,并对账验证 |
延伸阅读
「延伸阅读」小节链接- Sam Newman《Building Microservices》第 5 章(数据库集成)
- Chris Richardson《微服务架构设计模式》第 5 章(Saga 与数据一致性)
- Martin Fowler 关于 Database Per Service 模式的论述