2. 服务边界定义:微服务架构下缓存归属权与数据所有权的划分原则
在微服务架构中,缓存不再是单体应用中的一个统一组件,而是分散在各个服务边界内的独立资源。错误的缓存归属权划分会导致数据不一致、缓存穿透、以及跨服务调用的复杂协调问题。本章的核心原则是:谁拥有数据的所有权,谁就拥有该数据的缓存归属权。
2.1 数据所有权与缓存归属权的耦合关系
数据所有权定义了哪个服务对数据的写入、更新和删除负责。缓存归属权则定义了哪个服务负责管理该数据的缓存生命周期。两者必须严格对齐,否则会出现“多个主人”的混乱局面。
| 维度 | 数据所有权(Data Ownership) | 缓存归属权(Cache Ownership) |
|---|---|---|
| 定义 | 对数据记录具有写入权限和一致性责任 | 对缓存键的创建、更新、失效策略负责 |
| 典型归属 | 领域服务(Domain Service) | 数据所有者服务本身 |
| 反模式 | 多个服务同时写入同一数据源 | 非所有者服务直接操作缓存键 |
| 后果 | 数据冲突、更新丢失 | 缓存与数据库永久不一致 |
核心原则:缓存是数据所有者的私有资源。其他服务只能通过所有者提供的 API 或事件来间接影响缓存状态,绝不能直接写入或删除不属于自己的缓存键。
2.2 服务边界划分的三种模式
根据业务场景的耦合程度,服务边界下的缓存归属权可以划分为以下三种模式:
- 独占缓存模式(Private Cache):缓存完全属于单个微服务,其他服务无法访问。这是最安全、最简单的模式,适用于高内聚的领域服务。
- 共享缓存模式(Shared Cache):多个服务共享同一个缓存实例,但每个服务只操作自己拥有所有权的数据键。需要严格的键命名空间隔离。
- 全局只读缓存模式(Global Read-Only Cache):数据所有者写入缓存后,其他服务通过只读方式访问。适用于配置信息、基础字典等变更频率极低的数据。
实践建议:优先选择“独占缓存模式”。只有当数据变更频率极低且读请求量极大时,才考虑“全局只读缓存模式”。共享缓存模式容易引发跨服务缓存污染,应尽量避免。
2.3 缓存归属权划分决策树
以下决策树可以帮助架构师在服务边界定义阶段快速判断缓存归属权:
2.4 缓存键的命名空间隔离
当多个服务必须共享同一个缓存基础设施(如 Redis 集群)时,必须通过键命名空间实现逻辑隔离。命名空间的设计应遵循以下规范:
# 推荐格式:{服务名}:{领域}:{实体类型}:{实体ID}:{字段}
user-service:user:profile:12345:avatar_url
order-service:order:detail:98765:status
# 反例:无命名空间,容易冲突
profile:12345
order:98765
| 隔离方式 | 实现方法 | 安全性 | 性能开销 |
|---|---|---|---|
| 键前缀隔离 | 每个服务使用唯一前缀 | 中等 | 无 |
| 独立 Redis DB | 每个服务使用不同的 DB 索引 | 高 | 低 |
| 独立 Redis 实例 | 每个服务部署独立的 Redis | 最高 | 高(资源成本) |
2.5 跨服务缓存失效的协调机制
当服务 A 更新了数据,而服务 B 缓存了服务 A 的数据副本时,必须建立缓存失效通知机制。以下是三种常见的协调模式:
避坑指南:不要使用“双写”模式——即服务 A 写入数据库后,直接去写服务 B 的缓存。这会导致缓存归属权混乱,且当服务 B 的缓存结构发生变化时,服务 A 必须同步修改,形成紧耦合。
2.6 服务边界定义清单
在微服务架构设计阶段,请使用以下清单来验证缓存归属权划分是否正确:
- ✅ 单一写入者原则:每个数据实体只有一个服务拥有写入权限。
- ✅ 缓存跟随所有权:缓存的管理权属于数据所有者服务。
- ✅ 只读访问通过 API:其他服务只能通过所有者提供的接口读取数据。
- ✅ 失效通知异步化:跨服务缓存失效优先使用事件驱动模式。
- ✅ 命名空间隔离:共享缓存基础设施时,必须使用服务前缀隔离键空间。
- ✅ TTL 兜底:所有缓存项必须设置合理的过期时间,防止永久不一致。
工程落地要点:在服务契约文档中明确标注“缓存归属权”字段。例如,在 OpenAPI 规范中为每个接口添加
x-cache-owner: user-service 扩展属性,方便架构治理工具自动校验。