怎样实现跨微服务的数据查询?
微服务是很好,但如果每个服务都有自己的数据库,那怎么连接不同服务的数据,从而提供统一的查询呢?这篇文章按照微服务改造的不同阶段,给出了解决方案。原文:MicroServices: How do we join the data among different microservices?[1]
如何连接不同数据库中的数据?
Monolith 和 Microservices 数据库结构比较
让我们从一个简单的问题开始:假设我们有一个需要进行微服务改造的单体系统,这个系统用于支持用户通过邮件购买产品。那么问题来了,在微服务化后,我怎么能够知道通过邮件购买产品 A、B 或 C 的所有用户?
就像 IT 世界的任何事情一样,没有绝对正确或错误的解决方案,根据具体用例,一种方案可能比另一种更合适。我们始终需要平衡不同解决方案的利弊,并将其应用到特定环境和开发文化中。有时候,快速的解决方案可以帮助我们在市场中生存下来,而持久和复杂的解决方案可以帮助我们在未来迅速扩大规模!
本文给出的解决方案将涵盖我们在微服务之旅中所经历的任何情况,并解释其利弊。
使用单一数据库集群(利用 Schema 或表)
使用单一数据库集群的微服务数据架构
如果不同的微服务都访问同一个数据库集群,这种解决方案会很有用,其主要思想是为不同的服务定义不同的 Schema/表。
每个服务负责往自己的表里写入其数据,从其他服务的 Schema/表中读取数据
经验法则
写数据:只有拥有数据的微服务的写入请求才被允许。
读数据:任何需要数据的服务都可以读取数据。
如果决定在同一个数据库中使用该模式,需要更新所有查询以启用交叉连接。以上面的例子为例,假设我们想知道所有购买产品 A、B 或 C 的用户。
好处:
如果要连接的 Schema 和表使用相同的字符集以及相同类型的外键,则可以利用索引。
从开发的角度来看,更容易实现,一切都基于 SQL 查询。
这是开始进行微服务化改造的第一步,能够了解系统是否设计良好,并准备好完全解耦。
坏处:
单一数据库集群意味着单点故障乘以系统中微服务数量,只需要有一个微服务执行了一个消耗资源的查询就可以影响到所有其他的微服务。
需要为每个服务创建一个用户,并确保将用户配置为在特定表或特定 Schema 上写/只读。
使用多个数据库:复制数据
两个微服务及其数据库,没有复制数据,因此无法连接数据以检索用户的电子邮件
当我们把数据从单一数据库集群中移出时,就失去了在数据之间进行连接的能力。因此,如果我们仍然需要进行大量数据的连接并创建一个冗长的报告,怎么办才好呢?一种简单的方法是进行数据复制。即使我们能够想到一个解决方案解决大量数据连接的问题,但并不一定有时间构建和实现这样的解决方案。
黄金法则
写数据:只允许与数据库相关的微服务写入数据(例如,服务 1 只能在数据库服务 1 中写入)。
读数据:允许从复制数据的任何数据库中读取数据。
好处:
在微服务所在的数据库中数据是可用的,因此可以执行 join 操作。
只能复制需要的数据。
非常快,大部分时间很容易配置。
坏处:
需要设置规则,不允许微服务修改复制的数据。
数据是近实时可用的。
需要一个适当的流程来复制数据。
如果复制失败,数据将不同步。
使用 AWS DMS 复制数据
如果需要将数据从一个数据库集群复制到另一个数据库集群,可以使用 AWS DMS(数据迁移服务)。该服务允许从两个不同的数据库复制数据,并且允许应用基本的 ETL 转换。
如果只想传输部分数据,或者需要更改列名等信息时,ETL 转换非常方便。使用 AWS DMS 的唯一问题是索引,编写管理索引的简单方法的时机是:
启动和停止复制(需要在复制数据库上创建表/模式)
手动应用索引
再次启动正在进行的复制
拥有一个完整的副本意味着仍然能够像之前那样执行 join 操作,实现接近实时的读取。
事件(AWS kineesis 或 AWS Lambda)
如果你正在使用事件驱动架构(EDA,Event-Driven Architecture),AWS Kinesis 或者 SQS 和 Lambda 的组合绝对值得一看!然而,即使你没有使用 EDA,而只用 AWS,也会发现这个场景非常有趣。
如果你已经部署了 AWS RDS Serverless,并且希望将数据复制到另一个数据存储中,不幸的是,没法用 AWS DMS,原因很简单,AWS RDS Serverless 没有 binlog(这会在下个版本中修复)。
当我们需要多个参与者侦听数据并对更改采取行动时,这种方法是最好的。本例中,User 服务将发出一个事件,表示他的域中的一些数据发生了更改。作为侦听器的 Product 服务将接收更改并修改数据。
AWS Kinesis 可以被直接调用微服务的简单 REST API 所取代。
节约生命法则
永远尝试在当前场景中 1:1 的复制数据,这将在灾难发生的时候拯救你!即使引入了一个错误,并且侦听器没有正确插入数据,也可以在当天修复这个错误。我们可以将数据库 A 转储到数据库 B 中,而不是从头开始重新处理所有事件。
使用单个或多个数据库:API 调用
在构建微服务时,应该始终牢记 API First,这将使服务之间无缝沟通。
在这个例子中,如果我们想获取某个用户购买的产品记录,非常简单,只需要向 Product 服务询问我们想要查询的用户的 ID,然后使用 Product 服务检索我们要查找的数据。
在本例中,我们希望检索购买产品 A、B、C 的所有用户的电子邮件,因此,可以遵循以下操作流程:
好处:
数据是完全独立和隔离的
每个微服务可以彼此通信,请求关于其他微服务领域的信息
坏处:
网络延时
查询信息越多,操作就会越慢。想象一下为 5000 万用户请求信息。
API 批量调用
如果数据库中有很多用户,可以使用批量调用加快信息检索。
好处和坏处和之前的解决方案差不多,根据使用的架构,可以使用一些多路复用(多线程/协程)技术加速。
聚合服务
当我们希望以接近实时的方式处理数据时,聚合服务是很好的选择。有很多种具体实现的技术,可以混合使用直接 API 调用、事件或数据转储,都没有关系。
在本例中,将向你展示如何使用事件构建解决方案并传递不同的服务结果。
发出事件的服务和接收事件的其他服务
如果 Product 服务和 User 服务发出事件,聚合服务可以接收事件并根据需要处理数据。下面以 Service 3 为例,给出一个更详细的示例:
监听并处理事件,将事件存储在一个新的数据存储中
监听事件,一旦收到事件,就进行需要的转换,然后将它保存到 ElasticSerach 中以方便检索。
结论
通信是微服务的基础,在实现本文(或资源部分中提到的书)中的任何解决方案之前,要权衡你的资源并了解你的用例。
资源
O’Reilly: Monolith To Microservices[2]
O’Reilly: Building Microservices[3](中文版:《微服务设计》[4])
References:
[1] https://medium.com/creditorwatch/join-data-among-microservices-1cda360c6c1c
[2] https://www.oreilly.com/library/view/monolith-to-microservices/9781492047834/
[3] https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/
版权声明: 本文为 InfoQ 作者【俞凡】的原创文章。
原文链接:【http://xie.infoq.cn/article/c420ff9401dc8def2b2cf53ec】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论