你的 Microservices 不可扩展。你的 Database 正在哭泣。
Source: Dev.to
隐藏的瓶颈
我们最初把一切都归咎于显而易见的东西之外:“也许我们需要更多副本。”
事实更简单,也更让人不舒服:我们的微服务并不是问题所在。
微服务卖的是一个诱人的理念——让系统的每个部分都可以独立扩展。实际上,大多数团队的做法是:
- 将应用拆分成 10–20 个服务。
- 把它们全部指向同一个数据库。
- 称之为 “微服务架构”。
结果是一个 分布式单体,伴随网络延迟。每个服务可以水平扩展,但所有流量仍然汇聚到同一个瓶颈。负载上升时,数据库出现混乱,而不是微服务本身。
症状
- 延迟突增,但没有错误或崩溃。
- 连接数增多、只读副本延迟、连接池耗尽。
- 锁堆积在无人监控的地方。
看似无害的单独改动,组合在一起会变成灾难:
| 服务 | 改动 | 额外查询 |
|---|---|---|
| A | 新增端点 | +3 条查询 |
| B | “只加个 join” | +2 条查询 |
| C | 每 5 秒轮询一次 | – |
扩展的影响
把一个服务从 2 个 pod 扩展到 20 个 pod,不只是吞吐量的线性增长;它会成倍增加:
- 打开的连接数
- 空闲事务
- 并发写入
- 缓存未命中
- 锁争用
数据库把每个 pod 当作一个新来的陌生人,积极请求资源,即使仪表盘上显示 “服务延迟看起来还好”。
缓存的诱惑
大多数团队会加入:
- 用于读取的 Redis
- 带有情感化 TTL 的 HTTP 缓存
缓存让系统变快……直到它不再快,因为:
- 写操作仍然落在同一个数据库上
- 缓存失效很快变得混乱
- 跨服务的数据一致性变成猜谜游戏
- 运维复杂度上升,却没有消除耦合
缓存是止痛药,而不是根治方案。
未能解决的问题
- 更大的数据库实例
- 更多副本
- 更高的连接上限
- 在站会上大喊 “优化查询”
有效的做法
每个服务拥有自己的数据。 这点必须坚持。
如果另一个服务需要这些数据,它必须:
- 调用 API,或
- 消费事件,或
- 从专门构建的读模型中读取
不要出现 “只要跨服务一次 join”。这种模式只会让数据库的痛点重新燃起。
我们用以下方式替换了同步依赖:
- 事件
- 异步工作流
- 最终一致性更新
并不是所有东西都必须瞬时完成;大多数系统只需要可靠。
思维方式的转变
我们不再问 “这个服务能扩展吗?” 而是问 “在流量提升 10 倍时,这会对数据库产生什么影响?” 这唯一的问题重新塑造了我们的架构评审。
微服务并不会自动带来可扩展性。它们提供了选项——但代价是纪律。没有严格的边界,它们会放大数据库问题,而不是解决它们。
要点
- 拥有自己的数据。 每个服务应拥有自己的 schema 或数据库。
- 避免共享表,跨服务共享表会产生隐藏耦合。
- 为流量模式设计。 理解 pod 扩展如何成倍增加数据库负载。
- 优先使用异步通信 而非同步 join。
- 将数据库健康(连接数、复制延迟、锁争用)视为一等关注点进行监控。
如果你的系统在流量增加时变慢,不要只盯着服务本身。请检查:
- 谁拥有数据?
- 有多少服务触及相同的表?
- pod 扩展如何成倍增加数据库负载?
- 你的架构是否匹配流量模式?
因为十有八九,当 “微服务无法扩展” 时……其实是微服务在扩展,只是你的数据库在哭求帮助。