背景
我们假设你正在使用微服务架构来开发一个在线商城应用。大多数服务需要用某种数据库来持久化数据,如:订单服务存储关于订单的数据,客户服务存储客户相关的数据。
问题
微服务中数据库架构是什么样的?
限制服务之间必须松耦合,以使得每个服务可以独立开发、部署和扩展
一些业务事务必须保障跨多个服务的业务不变量。比如,下单这个用例将会验证新订单是否会超过顾客的信用额度限制。其他业务事务必须通过多个服务更新数据。
一些业务事务需要能查询分布在多个服务中的数据。比如,查看可用的信用额度必须要先查询客户再去查询其信用额度来计算下单的总额。
一些查询必须要join分布在多个服务中的数据。比如,查询某个地区的客户和他们的最近订单需要join客户服务数据和订单服务数据。
数据库必须能被复制和分库来实现扩展。见伸缩立方(Scale Cube)
不同服务有不同数据存储要求。对于一些服务,关系数据库是一个好的选择。其他服务可能需要一个像MongoDB的NoSQL数据库,更适合存储复杂、无结构化的数据。
方案
保持每个微服务的数据只对其服务访问,并且只能通过服务的API对外访问。一个服务的事务只涉及其对应的数据库。
下图展示了相关架构:
服务的数据库实际上是该服务实现的一部分,它不能被其它服务直接访问。
这有几种方式来保持服务的数据私有,你不需要为每个服务准备数据库。比如,如果你正在使用关系数据库,那么有这么几种选项:
每个服务多个私有table:每个服务拥有一些只能被该服务访问的私有tables。
每个服务一个Schema:每个服务拥有一个只能被该服务访问的私有Schema。
每个服务一个Database:每个服务拥有一个Database Server。
每个服务多个私有Table和每个服务一个Schema具有最低的开销,使用每个服务一个Schema更吸引人,因为这样会使得权限比较清晰。一些高吞吐量的服务可能需要他们自己的database server。
创建这种边界来使得模块化是一个好主意。这样以来你就可以为每个服务分配不同的数据库用户和数据库控制权限。如果没有这种边界,开发者将总会绕过服务的API来直接访问数据库获取数据。
结果
使用一个服务一个数据库有以下好处:
确保服务之间松耦合,对一个服务数据库的更改不会影响到其他服务。
每个服务可以使用更适合的数据库类型。如做全文搜索的服务可以使用ElasticSearch。
但也有以下缺点:
实现跨多个服务的事务并不简单,由于CAP定理,最好避免分布式事务,而且许多流行数据库(如NoSQL)不支持分布式事务。最好的解决方案是使用Saga模式,当服务中的数据更新时发布一个事件,其他订阅了该事件的服务更新其数据来响应该事件。
实现在多个数据库join数据具有挑战性。
管理多个SQL和NoSQL数据库的复杂性。
这也有几种方案:
API组合:由应用程序实现join数据而不是数据库。假设一个服务要获取客户和他最近的订单,则先从客户服务获取该客户数据,再从订单服务查询获取该客户的最近订单信息。
命令查询职责分离(CQRS):维护一个或多个来自其他服务的物化视图。服务订阅其他服务的事件,当其他服务发布事件时该服务再更新相关的物化视图。假设一个在线商城要实现查找特定区域的客户和他们最近的订单这个查询,只要通过维护一份join了客户信息和订单信息的物化视图就行了。该视图由该服务通过订阅客户服务和订单服务的事件来进行更新数据。