CQRS是提高了一致性

16-10-10 banq
                   

这是Reactive微服务框架lagom作者一篇文章,针对CQRS误解进行驳斥,这个误解观点是:

CQRS的问题是,它使事情变得更加复杂,因为它降低了一致性。

如果你有一个传统的单片整体系统,所有的数据操作都针对一个数据库,这个唯一的数据库支持ACID事务,那么答案应该是的,当这个系统切换到cqrs会降低一致性。然而,传统这种单片系统并不是cqrs通常出现的背景,CQRS的语境是针对微服务,如今越来越多的行业专家建议人们远离单片整体系统迁移到微服务。

微服务降低一致性

微服务的核心原则应该是服务访问自己的数据。另一个核心原则是,他们应该能够自主自治的。这意味着他们不需要依赖其他服务-尤其是同步调用其他服务。

如果一个服务是自治的,它需要存储所有必要的数据,才能实现其功能。有时,这个数据可能被其他服务拥有-因此,为了实现功能要求,其他那些服务需要发布数据到该服务,让它来获得后并更新自己的存储。

这意味着在微服务系统,相同的数据将被存储在许多地方。数据能在整个系统实现强一致性吗?除非每次你做一个操作时,停止整个系统所有其他请求处理,直到这个操作成功为止,答案是否定的,所以,它会经常是不一致的。

所以,切换到微服务本质上是降低一致性。一致性降低是一个微服务的必然结果。

不一致性是非常坏的

因为使用微服务,我们的系统数据集会变得不一致,我们必须处理这种不一致性,通常在一个分布式系统中处理不一致性是使用最终一致性。但实际上是如何实现的呢?

让我们想象一下服务B需要一些被服务A拥有的数据来实现它的功能责任。在这种情况下,A是负责写入数据的,它是处理这种写命令的,而B是负责读取数据的,它做的是查询。为了让B自主自治,B就不能直接通过A查询数据,它需要A推送给它数据,这样B可以更新自己的查询存储,以后需要查询使用这些数据时,B就在本地这些存储中查询数据。

理论上服务A可以通过同步方式做这些事,当一个写操作在服务A上完成时,它会将新结果通过调用服务B告诉它。这在纸上谈兵会工作很好,但当服务B在那个时候发生奔溃怎么办?服务A必须自己保持重试直到服务B恢复正常吗?如果服务A自己也当机了呢?

正如你看到我们现在有一个一致性的问题,而且不会最终成为一致的问题。服务A已被更新,但服务B未能更新。它们将永远保持不一致,我们可以说,它们已经确定是不一致的,并将需要人工干预以修复.

出现这个问题的原因是,我们将写操作命令和查询读职责两者放在同一个操作中实现了,因为这种操作不是原子,操作中发生任何故障将导致结果不一致。

CQRS又将一致性给你送回了

cqrs意味着命令职责与查询职责分离。在我们这个场景中,使用cqrs,服务A处理命令,并更新自己的状态,就完成了。然后,在另外一个单独的操作中,另一个进程将获取A操作的结果,并异步将其推到服务B。

由于操作是异步的,服务B不需要是实时正在运行-比如它可以使用一个消息队列来处理消息。如果服务B没有运行,那么系统将不一致持续一段时间。然而,当服务B恢复,就会处理消息,系统将再次成为一致的,这就是最终一致的。

通过采用cqrs,我们能够找回一些一致性保证,这些都是我们迁移到微服务时失去的。我们没有一个全局一致的系统,但我们可以保证最终一致的系统。

CQRS是必要之恶

相比传统将命令与查询混合在一个操作中,针对是一个强一致性数据库,CQRS是复杂的。然而,在微服务的世界,我们又不能使用一个强一致性数据库。

在微服务世界,不一致是必然的。如果有人说在微服务中使用cqrs意味着你失去了一致性,他们没有认识到实际上在他们开始使用微服务已经失去了一致性,不是cqrs让他们失去了一致性。相反,cqrs是一个非常强大的工具,使我们能够解决微服务中固有的不一致,带给我们的是最终一致性而不是数据结果的不一致。

all that jazz - CQRS increases consistency

                   

5