手记

微服务架构之分布式数据管理

对于一个单体的应用程序来说, 通常会连接一个关系性数据库, 使用关系型数据库的好处就是应用程序可以很好的保证事务的特性, 主要是原子性, 一致性,隔离性和持久性.

原子性: 事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发生。

一致性:
在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。如A给B转账,不论转账的事务操作是否成功,其两者的存款总额不变(这是业务逻辑的一致性,至于数据库关系约束的完整性就更好理解了)。

隔离性:多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。

在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。事务最复杂问题都是由事务隔离性引起的。完全的隔离性是不现实的,完全的隔离性要求数据库同一时间只执行一条事务,这样会严重影响性能。所以我们设置和数据的隔离级别

持久性: 在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

所以, 对于单体应用程序来说, 我们只需要开始一个事务, 然后执行增删改查操作, 然后提交事务。

使用关系型数据库的另一个好处是他提供了SQL, 我们不需要关注访问数据库的底层细节。而且, 因为只有一个数据库, 查询很方便。

在我们使用微服务架构之后, 数据访问将变得越来越复杂, 因为每个微服务的数据是私有的,我们只能通过它提供的API来进行访问。

将数据封装到服务里面,可以保证松耦合,每个微服务之间可以互相独立。如果多个服务访问相同的数据, 那么数据Schema的更新将需要通知所有的服务。

更复杂的是, 不同的微服务甚至使用不同类型的数据库, 在现代的应用程序中,关系数据库有时候并不是最佳选择, 有时候使用NoSQL更好,同时性能和扩展性也比较好。 对于查询文本信息时, 使用ElasticSearch会更好,有时候会使用NEO4J, 因此, 在微服务架构中, 数据库往往是关系型和NoSQL的结合。

一个分布式的多类型数据存储架构, 实际有很多的好处, 如前所述, 松耦合和较好的性能和扩展性。但是, 这对我们进行分布式的数据管理 提出了新的挑战。

主要是:

  1. 怎么实现业务事务来保证多服务之间的一致性。

服务1 只能访问服务1的数据, 不能访问服务2的数据, 这样的话, 服务1里面有个事务需要服务1 和服务2 的数据,理论上,可以使用分布式事务(两阶段提交),但是对于NoSQL数据库来说, 并不支持这种,所以这是一个难题。

  1. 怎么实现从多服务查询数据

例如,假设应用程序需要显示客户及其最近的订单。如果订单服务提供用于检索客户订单的API,则可以调用API来检索此数据。应用程序从客户服务中检索客户,并从订单服务中检索客户的订单。但是,假设订单微服务只支持通过订单ID来查找订单(可能它使用的NoSQL数据库只支持基于主键的检索)。在这种情况下,我们将无法来检索所需的数据。

解决方案就是基于事件驱动的架构:

基于事件驱动的架构,就是当某个微服务做了一些重要的改动的时候(例如, 修改了某个业务数据表), 这个微服务会发布一个事件,其他相关的微服务会订阅这个事件,这些微服务接收到这个事件以后,会更新自己的业务数据表或者实体,而且还可能引发更多的事件被发布。

我们可以使用事件来实现跨多个微服务的业务事务, 每个事务包含很多步骤, 每个步骤会有一个微服务, 这个微服务会更新一个业务数据实体,并且发布一个事件,触发下一个步骤。 微服务之间通过消息代理来交换事件。

下面举例说明:

基于事件驱动的方法, 实现创建订单的时候,检查客户的余额信息。

  1. 1 订单微服务创建一个订单,状态是“新订单”, 然后,发布一个订单创建的事件。

  2. 2 客户微服务收到订单创建的事件, 从账户余额里扣除, 发布一个扣除余额的事件。

  3. 3 订单微服务收到一个扣除余额的事件, 将订单状态修改为“生效”。

更复杂的场景可能是,在检查客户余额的过程中,还需要检查库存。

我们假设基于事件驱动的架构中,

  1. 每个微服务原子性的更新数据库,并且发布一个事件。
  2. 消息代理保证这个事件可以至少一次被发送到订阅的微服务。

我们就可以实现跨服务的业务事务。但是,这并不是ACID事务, 它并不能保证最终一致性。

我们也可以定义一些微服务来组合对外提供一些服务,例如客户订单更新的微服务,客户订单查询的微服务。

在客户信息微服务或者订单微服务发布信息修改事件的时候, 客户订单更新微服务就会消费这个事件并且更新客户和订单数据库视图, 查询客户订单关系就可以直接通过客户订单查询的微服务来完成。

基于事件驱动的微服务架构的优点是:

  1. 支持跨服务的事务实现,而且提供最终一致性。
  2. 支持实现一些视图性的微服务。

缺点是:

  1. 编程模型很复杂。如果事务中某阶段发生故障,需要实现程序级的事务回退,例如:上面例子中,余额查询失败,系统必须取消订单。
  2. 系统必须处理不一致的数据。有些视图还没更新,系统会看到不一致的数据等。
  3. 订阅的微服务必须忽略重复的事件。
1人推荐
随时随地看视频
慕课网APP