只读业务数据访问
如果需要读取其他数据库中的动态业务数据,最理想的方式是调用服务。如果只是调用其他微服务做一些计算,性能一般可以接受。如果你需要做数据连接,你可以用程序代码来做,而不是 SQL 语句。如果测试后性能不能满足要求,那么可以考虑在自己的数据库中建立一组只读数据表。同步数据的方法大致有两种。如果是事件驱动的方法,则通过发送消息进行同步。如果是RPC方式,则使用数据库自身或第三方同步软件提供的同步方式。
通常,您可能只需要来自其他数据库的几个表,每个表只有几个字段。此时,另一个数据库是最终的数据来源,控制着所有的写操作和相应的业务验证逻辑,我们称之为主表。可以从表中调用您的只读库。当一条数据写入主表时,会发送一条广播消息,所有拥有从表的微服务都会监听该消息并更新只读表中的数据。但是此时你必须非常小心,因为它比静态表危险得多。首先,它的表结构变化更频繁,而且它的变化完全不受你的控制。第二个业务数据不像静态表,更新频繁,所以对数据同步的要求比较高。可接受的延迟时间取决于具体的业务需求。
它也有两个问题:
除非你可以使用服务调用(没有本地只读数据库)来完成所有的功能,否则无论你使用RPC还是事件驱动的微服务集成,上面提到的问题都是不可避免的。但是,您可以通过合理规划数据库更改来减少上述问题的影响,下面将详细说明。
读写业务数据访问
这是最复杂的情况。通常,您有一个表是主表,而其他表是从表。主表包含主要信息,这个主要信息被复制到从表中,但是微服务会有额外的字段需要写入从表。这样本地微服务就对从表进行了读写操作。并且主表和从表有顺序关系。从表的主键来自主表,所以必须先有主表,再有从表。
上面的图片是一个例子。假设我们有两个与电影相关的微服务,一个是电影论坛,用户可以在其中发表电影评论。另一个是电影店。 “movie”是分享表,左边是电影论坛库,“movie”表是主表。右边是 Movie Store 库,它的“电影”表是从表。它们共享“id”字段(主键)。
主表是数据的主要来源,但从表中的“”和“价格”字段不在主表中。向主表插入数据后,发送消息,从表中接收消息,并在本地“电影”表中插入一条数据。而且从表中也会修改“”和“价格”表中的字段。在这种情况下,要为每个字段分配唯一的源(微服务)问道私服连接到数据库怎么修改,只有源有权主动更改字段,其他微服务只能被动地更改(在收到源的更改消息后)。
在此示例中,“”和“价格”字段的来源是右侧的表格,其他字段的来源是左侧的表格。本例中“”和“价格”只存在于从表中,所以数据写入是单向的,方向是从主表到从表。如果主表也需要这些字段,那么就得回写,数据写入就变成双向了。
直接访问其他数据库
这种方法是绝对禁止的。生产环境中的许多错误和性能问题都是以这种方式创建的。以上三种方式新建一个本地只读数据库表,导致数据库物理隔离,使得一个数据库的性能问题不会影响另一个数据库。另外,当主库中的表结构发生变化时,可以暂时保持从库中的表不变,这样程序仍然可以运行。如果直接访问别人的库,一旦主库被修改,其他微服务程序会立即报错。见。
向后兼容的数据库更新
从上面的讨论可以看出,修改数据库表结构是一件影响广泛的事情。在微服务架构中,共享表在其他服务中也有只读副本。现在当你想改变表结构时,你还需要考虑对其他微服务的影响。在 () 架构中,数据库更新是向后兼容的,以确保应用程序部署可以回滚。
兼容性的另一个原因是支持蓝绿版本。在此部署中,您拥有代码的旧版本和新版本,负载平衡确定每个请求转到哪个版本。他们可以共享一个数据库(这要求数据库向后兼容),或者他们可以使用不同的数据。有几种类型的数据库更新:
向后兼容的数据库更新的好处是,如果程序部署出现问题,它可以回滚。只需回滚程序,而不是数据库。回滚时,一般只回滚一个版本。本次部署不会修改所有需要删除的表或字段。一个或几个版本后,确认没有问题再删除。另一个好处是它不会对其他微服务中的共享表产生直接的直接影响。当这个微服务升级时,其他微服务可以评估这些数据库更新的影响,然后决定是否进行相应的程序或数据库修改。
跨服务交易
微服务的难点之一是如何实现跨服务事务支持。两阶段提交(Two-Phase)已经证明性能不理想,现在基本不用了。一致认可的方法称为 Saga。
它的工作原理是为事物中的每个操作编写一个补偿操作( ),然后在回滚阶段一个一个地执行每个补偿操作。例如,如下图所示,一件事有3个操作T1、T2、T3。每个操作都应该定义一个补偿操作,C1、C2、C3。事务执行时,T1按正序先执行,回滚时,C3倒序先执行。
事务中的每一个操作(前向操作和补偿操作)都被包装成一个命令( ),Saga 执行协调器(Saga(SEC))负责执行所有命令。在执行之前,所有的命令都会按顺序存储在日志中,然后 Saga 执行协调器从日志中取出命令,依次执行。当执行中发生错误时,错误也会写入日志,所有正在执行的命令都停止,开始回滚操作。
Saga放宽了对()的要求,它可以保证最终的(),所以在事物执行过程中数据是不一致的,这种不一致会被其他进程看到。生活中,很多时候,我们对一致性的要求并没有那么高问道私服连接到数据库怎么修改,暂时的不一致性是可以接受的。比如银行转账操作在执行过程中并不是在一个数据库事务中执行,而是以记账的方式分为两个动作,这样也保证了最终的一致性。
Saga 的原理看似简单,但要正确实施却很难。它的核心问题在于错误的处理。要完全解释它,需要写另一篇文章。我现在只谈要点。网络环境不可靠,正在执行的命令可能很长时间没有返回结果。在这种情况下,首先,您需要设置一个超时。二、因为不知道没有返回值的原因是命令已经执行但是网络有问题,或者在完成之前牺牲了,所以不知道是否执行补偿操作。这种情况下正确的做法是重试原来的命令,直到确认完成,然后再进行补偿操作。但是对命令有一个要求,就是操作必须是幂等的(),也就是说可以多次执行,但最终的结果还是一样的。
另外,有些操作的补偿操作比较容易产生,比如支付操作,你只需要退钱。但是对于一些操作,比如发邮件,完成后是没有办法回到之前的状态的,只能再发邮件来更正之前的信息。因此,补偿操作不一定要回到原来的状态,而是取消原来操作的效果。
微服务的拆分
我们最初的大部分程序都是单体程序,但现在我们需要将它们拆分为微服务。我们如何才能减少对现有应用程序的影响?
我们以上图为例。它有两个程序,一个是“app”,一个是“app”,它们共享下图的数据库,库中有“core”、“core sku”、“core item”三个表。
假设我们要拆分出一个名为“-”的微服务,它需要访问“核心”表。第一步是将程序从原始代码中拆分出来,变成一个服务。数据库没有移动,服务仍然指向原始数据库。其他程序不再直接访问该服务管理的表,而是通过调用该服务或创建另一个共享表来获取数据。
第二步,拆分服务的数据库表。这时微服务有了自己的数据库,不再需要原来的共享数据库。至此,它就变成了真正的微服务。
上面只讲了拆分一个微服务。如果有多个微服务需要拆分,需要按照上面提到的方法一个一个拆分。
另外,他的文章“闯入”中有一个很好的建议。也就是说,当你将服务从单体中拆分出来时,不要只考虑拆分代码。由于当前的要求可能与原来的不同,原来的设计可能不适用。而且,技术也更新了,代码也要相应地进行转换。更好的方法是重写原始功能(而不是重写原始代码),专注于拆分业务功能而不是拆分代码,并使用新的设计和技术实现该业务功能。
结论
数据库设计是微服务设计的重点。基本原理是每个微服务都有自己独立的数据库,只有微服务本身可以访问这个数据库。微服务之间的数据共享可以通过服务调用,或者主从表来实现。共享数据时,找到正确的同步方式。在微服务架构中,对数据库的修改具有广泛的影响,需要保证向后兼容。跨服务实现事物的标准方法是 Saga。将单体程序拆分为微服务时,可以分步进行,以减少对现有程序的影响。
最新评论