使用事件溯源摆脱关系数据库?

Posted

技术标签:

【中文标题】使用事件溯源摆脱关系数据库?【英文标题】:Use Event Sourcing to get rid of relational database? 【发布时间】:2015-11-09 00:28:40 【问题描述】:

我最近阅读了一些关于 CQRS 和事件溯源的文章。虽然第一个在我看来是一个非常复杂和冒险的解决方法,用于修复性能不佳的业务和设计不佳的数据访问层和数据模型,但最后一个似乎是许多问题的解决方案。

使用事件溯源解决的问题:

    摆脱关系数据库和对象关系映射器,如 NHibernate 和实体框架。编程领域几乎没有人愿意关注诸如索引、表/索引碎片或规范化、如何设计关系数据以及如何编码/配置 ORM(其本身是一门科学)之类的东西。

    李>

    将业务模型和内存中的“数据库”结合在一起,一个实体/聚合服务将所有相关项保存在内存中,通过简单地将 CUD 事件转储到某个地方来保持完整性,而不会带来太大的痛苦。旧项目可以从内存中清除并转储到 NoSQL(或其他)存储并用于聚合计算、报告、搜索,并在必要时重新激活。如果我理解正确的话,像 VoltDB 这样的内存数据库以类似的方式使用事件转储,但仍然是关系数据库,与业务逻辑分离。

    这也将使并发更容易:而不是锁定(可能会出现完整的系统死锁)或具有一般“成功或失败”逻辑的乐观锁定,具体取决于数据是否同时更改(或相当复杂的数据库代码),合并规则可以在代码中实现。

    历史:实施审计功能、墓地表或“已删除”标记列或仍需要可能已删除的数据不再痛苦。

    数据复制/搜索/报告:使用全文索引而不是追逐丢失的关系索引,创建适当的查看区域,以所需格式为用户准备数据,而不是在关系数据库中使用丑陋的复制例程,使用触发器、后续存储过程甚至程序代码将数据复制到六个不同的表。

    版本控制:让许多模块运行多个不同的关系数据库版本是一件很痛苦的事情,每个版本都有不同的表和列,并且需要适当的 ORM 映射。在单层模型中可能更容易,事件转储接受任何对象格式(通常是无模式或松散模式 NoSQL 文档,表示为 JSON 或 XML)。也可以通过“数据架构更改事件”链升级旧数据(而不必维护关系数据库的迁移脚本)。

N 层业务模型/关系数据库/ORM 混乱

十年或更长时间前的 n 层方法可能是业务层和数据访问层。为了保持严格的分离,省略了许多关系特性,而是在业务层中实现它们:关系完整性,规范化,数据库是我所说的“垃圾转储”:看起来像一个在玩 SQL Server 的孩子管理工作室或访问。极度非规范化的多态引用(“外键”列引用不同的源表,由“ReferenceSource”标记标识),针对不同类型的业务对象滥用相同的表以及将数据复制到许多其他表(并从那里再次其他地方),因为性能不好,这应该可以改善查询。 ORM 的使用也没有对象引用,减少为单个对象加载和保存操作。加载聚合(实体/表行的图)将遍历图并为每组子实体发送查询。

当性能变得更差并且可能孤立引用造成严重问题时,可能会尝试实施经典的关系设计,但不可能使增长的系统适应完整的数据重新设计(没有人愿意为此付费),几乎没有人会知道如何在 ORM 中映射对象关系甚至优化加载。此类尝试仅限于设计中的少数几个地方,可能会使数据模型和访问更加难以维护。

n 层之上的 CQRS?

为了获得可接受的性能,可能会为某些模块构建单独的 SQL 查询,从而绕过具有单对象迭代访问的业务模型。这整个结构突然被称为事实上的 CQRS,因为单独的查询访问(可以通过良好实现的关系数据模型和 ORM 使用来处理,只要它不应该是“大数据" 类似 Google 或 *** 的工作负载),以及关系表中的大量重复数据,弥补了应用程序的即时访问。

比不恰当的表格格式更好?

好的,所以我阅读了 CQRS,虽然我不喜欢前面描述的“CQRS”的使用,但事件存储而不是关系数据库的概念看起来非常有用:它不太可能成功地执行引入原始的、最先进的关系数据库设计和 OR 映射,即使它也将是极其昂贵的。事实上,普通的面向对象编程比大多数数据库表更“规范化”,因为需要将所有内容都压入表格式或为对象图/聚合创建大量表。我同意:必须手动处理搜索索引和碎片整理、模式管理和数据历史跟踪,就像石器时代的 IT,就像在现代汽车和电动高速列车之外运行福特 T 模型和蒸汽机车。

有什么好的经验吗?

使用事件溯源(不一定是完整的 CQRS)的体验如何?它是否消除了关系数据库的大部分痛苦?我真的很期待一种集成了所有业务逻辑的内存数据库,并且可能足够快以使单独的查询模块变得可有可无!

【问题讨论】:

【参考方案1】:

这个问题有很多内容,因此不可能给出一个具体的、可操作的答案,但如果你正在寻找一个,那么它就是......

这取决于您的域。

CQRS/ES/DDD 并不适合解决每一个问题 - 它不是灵丹妙药。如果域表明 CRUD/NTier 足够好,那么这就是您应该使用的。您在问题中列出的所有问题都是基础设施或系统特征,并且没有提及应该告知您选择工具或实践的事情;你想构建什么?

【讨论】:

【参考方案2】:

虽然 CQRS、ES 和 DDD 经常一起使用,但它们是独立的概念,它们本身就非常强大。

CQRS(Command Query Responsibility Segregation):一般来说,这是一种非常有用的软件设计模式。这个想法是保留改变状态的事物(命令)与不改变状态的事物(查询)。在许多系统中,查询会修改数据库的状态,这使得开发人员很难推断正在发生的事情。

想象一下,进行查询以找出一些信息,并意识到信息因您查询而发生了变化。

CQRS 禁止此类行为。命令(不能返回信息)改变状态,查询(返回信息)不能修改状态。这样,您就可以确定代码的哪些部分是幂等的(因此可以在没有副作用的情况下随意调用)以及哪些部分会改变状态。

DDD(领域驱动设计):这是代码“数据结构”的设计方法。它没有规定数据库访问技术或许多技术细节。它的作用是提供指导和概念,以使应用程序中的数据更能响应实际用户的需求。它还简化了开发(尽管它不仅仅是将一些东西放在一起做更多的工作)。

ES(事件溯源):事件溯源是一种数据存储策略,它将数据存储从状态(一条数据在当前点的实际值time) 转换为 transitions(一段数据在其生命周期内发生的变化),称为 events

使用 ES 有几个优点。

首先,它允许企业存储更多关于之前发生的事情的信息(数据科学家的福音)。在传统系统中,数据更新会丢失大量信息,除非明确记录这些更新,否则信息将永远消失。这在 ES 中不会发生。

其次,存储所有事件使调试变得更加简单,因为现在开发人员可以从一开始就跟踪数据的处理。很久以前发生的对一段数据的更新(并且会被另一个更新重写并丢失)但可以识别和修复损坏的处理。此外,修复的效果甚至可以跨越在错误事件和最后一个事件之间发生的所有计算。在传统系统中,这是不可能的,因为我们只存储最新的状态。

虽然理论上可以编写一个事件源系统没有 CQRS 或 DDD,但要做到这一点要困难得多。

【讨论】:

以上是关于使用事件溯源摆脱关系数据库?的主要内容,如果未能解决你的问题,请参考以下文章

使用事件溯源和 CQRS 的缺点是啥?

「事件驱动架构」事件溯源,CQRS,流处理和Kafka之间的多角关系

事件溯源(Event Sourcing)小介

微服务、REST、事件溯源和数据一致性

PHP 事件溯源

同步关注点的事件溯源