使用微服务架构时如何保持数据库同步?

Posted

技术标签:

【中文标题】使用微服务架构时如何保持数据库同步?【英文标题】:How to keep DB in sync when using microservices architecture? 【发布时间】:2016-10-21 06:49:30 【问题描述】:

我即将学习微服务架构的工作原理。到目前为止,我认为每个微服务都需要自己的数据库,这是有道理的。

假设我们有一个客户微服务,它负责创建客户并返回客户列表。该服务将拥有自己的客户数据库。

假设我们在此服务上的负载非常高,因此我们选择横向扩展 20 倍。

我们有 20 个微服务,每个都有自己的数据库,所有服务都位于负载均衡器后面。

现在一个客户想要创建一个客户,负载均衡器向服务 9/20 发送客户请求,客户就创建好了。

在下一个请求中,同一客户希望确保已创建客户并希望查看客户列表,LB 在 11 月 20 日将其发送给服务。

现在我如何确保服务 9/20 将新创建的客户同步到服务 11/20 的数据库?

在 MSSQL 中,有一些功能可以在允许初始提交之前保持数据库同步,首先将数据保存在所有其他数据库中,但从长远来看,这种方法会产生问题,因为服务越多,时间越长提交需要花费多少时间?

【问题讨论】:

【参考方案1】:

转到多个数据库只会将软件架构的一个问题更改为分布式协调之一,后者,恕我直言,是一个更困难的问题。

人们建议使用事件系统,这意味着现在每个单独的服务都必须有自己的小解决方案来进行数据的分布式协调,ACID 已被淘汰。看看数据库环境,您会发现这不是一个容易或完全解决的问题。然后去分布式协调事务...

很多时候,您宁愿停机,也不愿让 N 个数据库处于完全未知的不一致状态。此外,对正常运行时间的看法具有误导性,是的,您的服务已启动,但如果它们对相同数据的视图不一致或丢失数据(丢失的事件),它们是否真的起作用?还是会产生不一致和错误的结果?

要么你有两个完全不依赖于拥有相同数据的服务,要么你需要一个共享一致的数据层。但是使用事件系统在 N dbs 之间进行复制并希望得到最好的,好吧,你的选择。

分布、持久性、一致性和可用性的问题应该在存储层处理,而不是应用层中的每个服务临时处理。制作这样一个系统需要许多人的谨慎和专门的专门知识,即便如此,也需要权衡取舍(CAP 定理)。

最后:大多数人希望微服务能够比通过单体应用更快地开发和发展他们的应用程序。处理每个微服务中存储的分布式协调和一致性将适得其反。

【讨论】:

完全同意!使用单个(池化)数据库来解决单点故障问题比引入多个必须与事件总线保持同步的数据存储要简单得多。【参考方案2】:

虽然可以为多个服务使用同一个数据库,但应该避免这种情况,因为它会在服务之间产生比预期更高的耦合。例如。数据库停机将影响所有共享服务,但如果每个服务都有自己的服务,则只会影响一个服务。

为了避免“分布式单体”服务相互进行同步调用(例如使用 REST),您可以使用基于流的方法。每当其数据更改时,每个服务都会发布一个更改事件,并且其他服务可以订阅这些流。因此他们可以对与他们相关的数据更改做出反应,例如通过在他们自己的数据库中存储数据的本地版本(以适合他们需要的表示形式,例如他们感兴趣的列 int )。这样他们就可以提供他们的功能,即使其他服务在一段时间内不可用。自然地,这种架构采用了最终一致性的语义,但在分布式系统中这通常是不可避免的。

设置此类数据流的一种方法是更改​​数据捕获 CDC,它将跟踪数据库日志文件(例如 mysql 中的 binlog)并为每个 INSERT、UPDATE 和 DELETE 发布相应的事件。一种开源 CDC 工具是 Debezium,它带有 MySQL、Postgres、MongoDB 以及(目前正在进行中的)Oracle 和 SQL Server 的连接器。它可以与 Apache Kafka 一起用作流式传输主干或 Java 应用程序中的库,允许您将数据更改流式传输到其他流式传输层,例如 Pulsar 或 Kinesis,只需一点代码。将持久主题用于更改事件的一个很好的优势,例如使用 Kafka,新服务可以出现并重新读取整个更改流(取决于主题的保留策略),或者只是获取每条记录的当前状态来为其本地数据库做一个初始种子。

(免责声明:我是 Debezium 的负责人)

【讨论】:

无需避免为同一服务的多个实例使用单个后备数据库。是的,需要考虑单个故障点,但是池化数据库是解决该问题的简单有效的选择。它是一个比 Kafka 事件流更简单的解决方案,并且可以保持多个数据存储同步。 我指的是不同的服务,而不是同一个服务的多个实例。 如果您小心地保持每个服务的数据“私有”,即使使用同一个数据库的不同服务也不会产生耦合。也就是说,每个服务都应该将自己的数据保存在自己的表甚至模式中。同样,任何服务都不应该直接从数据库中查询另一个服务的数据——如果一个服务需要访问另一个服务的数据,它应该调用该服务的公共接口而不是访问其邻居的私有数据。 当然,当必须共享数据库时,这就是要走的路。但这并不意味着没有耦合:服务共享相同的数据库技术和版本,数据库的停机时间会影响所有服务,它们会竞争 CPU 资源,等等。 @drhender 如果服务需要 NoSQL 而不是 RDBMS 怎么办?即使我们有使用自己的表或模式的私有数据,但负责微服务B的其他团队需要将列添加到由另一个团队?简而言之,在这些情况下,您将如何避免使用 Kafka?【参考方案3】:

这可以使用 CQRS 设计模式来实现,它通过遵循异步范式将实体的创建和查看分离。

在创建时,我们将实体持久性推送到 Kafka/RabbitMQ 并异步推送到数据库。可以在数据库上创建物化视图,从而加快检索速度。

【讨论】:

【参考方案4】:

每个微服务都需要自己的数据库

每个微服务都有一个单独的数据库不是先决条件(实际上也不是要求)。

您可以在同一个数据库上使用任意数量的微服务,但例如使用不同的架构。

微服务的有界上下文应该是边界。

假设我们在此服务上的负载非常高,因此我们选择向外扩展 20 倍。

扩展到相同微服务的 (X) 个实例并不意味着每个相同服务的每个实例都必须有一个单独的数据库。

大多数数据库在设计时都考虑了并发连接、用户和事务。单个数据库实例(具有一些乐观并发)可以优雅地处理数百个(如果不是数千个)并发连接。

如果您明确选择为同一服务的每个实例创建一个单独的数据库,那么您必须同步这些数据库。并且很可能会影响数据一致性。

以下是一些建议:

无论有多少实例使用它,每个微服务(而不是每个实例)都使用一个数据库。只有当您确定单个数据库无法处理负载时,才考虑每个实例一个数据库。

在 DB 之上使用共享缓存层(可能是 redis 缓存)

使用数据库集群来处理数据库的高负载/可用性。

【讨论】:

有时需要为每个微服务一个单独的数据库,例如,一个微服务需要提供全文搜索,在这种情况下,NoSQL 会是更好的方法。在这种情况下,您认为我们如何处理数据一致性?

以上是关于使用微服务架构时如何保持数据库同步?的主要内容,如果未能解决你的问题,请参考以下文章

如何保持两个国家的服务器的数据库同步

随行付数据同步中间件「Porter」开源啦

微服务架构的稳定性与数据一致性能如何快速提高?

异步微服务架构中的同步通信

魅族云同步的实践-协议和架构

RestKit:如何删除核心数据条目以保持内容与服务器同步?