事件溯源中的乐观锁


并发控制是在多用户环境中管理和协调对共享资源的并发访问的过程。在数据库和分布式系统的上下文中,并发控制旨在确保数据的一致性,同时允许多个事务并发执行。当不同的事务同时访问和修改相同的数据时,这尤其具有挑战性。

传统上,并发控制机制利用锁定技术来防止冲突和维护数据完整性。锁用于建立对共享资源的独占访问,确保一次只有一个事务可以修改它。虽然这种方法保证了数据的一致性,但当多个事务试图同时访问同一资源时,它可能会导致性能下降和争用增加。

引入乐观锁
乐观锁定提供了另一种并发控制方法,允许并发访问共享资源,从而提高系统性能。乐观锁定背后的关键思想是假设事务之间的冲突很少见,因此只有在必要时才应验证它们。这种方法减少了获取和释放锁的需要,最大限度地减少了争用并提高了可扩展性。

在乐观锁定中,每个事务都与表示事务开始时数据状态的版本或时间戳相关联。当事务要修改共享资源时,它首先检查资源的版本是否与它自己的相匹配。如果版本匹配,事务将继续进行修改。但是,如果版本不匹配,则意味着另一个事务在此期间修改了资源,从而导致冲突。在这种情况下,冲突的事务将被回滚,并且可以重试当前事务或者可以应用适当的解决策略。

了解事件溯源
事件溯源是一种模式,其中通过将一系列事件应用于初始状态来派生应用程序的状态。事件源不是直接存储当前状态,而是存储系统中发生的事件的日志。这些事件表示已经做出的更改,可以重放以重建应用程序在任何给定时间点的状态。这种方法提供了所有操作的历史记录,并允许轻松追溯和审计。

事件溯源中的乐观锁
乐观锁定可以有效地应用于事件溯源,以处理对系统状态的并发修改。目标是允许同时处理多个命令或事务,同时确保一致性并防止冲突。
在事件溯源中,每个聚合或实体都有一个相应的事件流,用于捕获与该实体相关的所有事件。事件溯源中的乐观锁定依赖于对事件流中的事件进行版本控制。版本号表示特定实体发生的事件的顺序或顺序。

执行命令或事务时,系统会检查实体事件流的当前版本。如果版本与预期版本匹配,则允许命令继续,并将生成的事件附加到流中。但是,如果版本不匹配,则表明另一个命令在此期间修改了实体的状态,从而导致冲突。

处理冲突
当由于版本不匹配而检测到冲突时,需要应用适当的冲突解决策略。以下是一些常见的方法:

  • 重试事务:一种直接的方法是在将预期版本更新为最新版本后重试事务。这允许冲突事务完成并确保后续事务在最新状态上执行。
  • 合并冲突的更改:在某些情况下,可以通过合并冲突的更改来解决冲突。这种方法需要分析冲突的性质并应用特定的合并策略来调和差异。
  • 使用业务规则解决:根据应用程序的要求,可以通过应用预定义的业务规则或策略来解决冲突。例如,可以通过考虑最近的更改或支持客户的偏好来解决购物车中的冲突更改。

在事件溯源中实施乐观锁
你的领域模型/核心逻辑不应该知道底层的并发控制机制。它应该通过对事件流中的事件进行版本控制,作为基础设施/适配器层中的横切关注点来实现。版本号表示特定实体发生的事件的顺序或顺序。

事件排序、交换性和乐观锁
当操作是可交换的时,这些操作的并发执行不会导致冲突或不一致。这是因为操作的执行顺序不会影响系统的最终状态。在这种情况下,可能不需要乐观锁定,因为不太可能发生冲突。

例如,考虑一个简单的银行账户系统,其中包含存款和取款等可交换操作。如果两个并发交易试图分别存入 100 美元和取出 50 美元,执行这些操作的顺序不会影响最终余额。无论存款发生在取款之前还是相反,结果都是一样的,即账户余额增加 50 美元。
在操作是可交换的情况下,系统可以依靠可交换性的固有属性来处理并发控制,而无需显式使用乐观锁定。这可以提高性能和可伸缩性,因为不需要验证或冲突解决。

然而,重要的是要注意并非系统中的所有操作都具有内在的可交换性。许多现实世界的场景都涉及非交换操作,其中执行顺序很重要。在这种情况下,仍然需要乐观锁或其他并发控制机制来保证数据的一致性。

查看演示应用程序,您可以在其中找到事件源中乐观锁定的示例,使用 FModel 库实现: