【问题标题】:Use cases of Event Sourcing, when we don't care about past states事件溯源的用例,当我们不关心过去的状态时
【发布时间】:2018-01-12 02:22:11
【问题描述】:

我一直在阅读有关事件溯源模式的文章,我已经看到它在我从事的项目中使用,但我仍然没有看到它的任何好处,虽然它使设计变得更加复杂。

也就是说,许多消息来源都提到,如果您想查看审核日志,能够重建 15 天前的状态,事件溯源很好,我看到事件溯源很好地解决了所有这些问题。但除此之外,还有什么意义呢?

是的,我可以想象,如果您处于关系世界中,那么写入会相对较慢,因为它们会锁定数据等等。但是解决这个问题要容易得多,通过使用 no-sql 并使用像 Cassandra 这样的东西。 Cassandra 的写入速度非常快,因为它们是仅附加的(有点像临时事件源),它的扩展性也很好。消息来源还提到事件溯源有助于扩展 - 它究竟如何帮助您进行扩展,而不是每个用户存储约 1 行数据,现在您有 9000 行,而不是检索单行,现在您正在重放 9000 行(或更少,如果您使设计更加复杂,并添加一些状态的时间快照并从最后一个快照重放当前状态)。

非常感谢事件溯源解决或链接的任何现实生活问题示例。

【问题讨论】:

    标签: sql cassandra event-sourcing nosql


    【解决方案1】:

    虽然我还没有实现分布式的事件源子系统(所以我不是专家),但我一直在研究和评估这种方法。事件溯源提供了许多关键优势:

    1. 可靠性
    2. 可扩展性
    3. 可进化性
    4. 审核

    我相信还有更多。在很大程度上,事件溯源的好处取决于您将其与之进行比较的基线(CRUD、事件驱动的 DDD、CQRS 或其他)和领域。

    让我们依次看一下:

    可靠性

    对于在系统更新时触发事件的事件驱动系统,您经常会遇到一个问题:如何同时更新系统状态并同时触发事件?如果第二次操作失败,则您的系统处于损坏、不一致的状态。事件溯源为此提供了一个简洁的解决方案,因为系统只需要一个操作来改变状态,这将自动地成功或失败:事件的写入。其他解决方案往往更复杂且可扩展性更低 - 2 阶段提交等。

    这在大型、高事务系统中是一个很大的好处,其中组件在事务进行时一直在发生故障、更新或更换。随时终止进程而无需担心数据损坏或一致性的能力是一个很大的好处,可以帮助您在晚上睡觉。

    在许多域中,您不会对相同的实体进行并发写入,或者您不需要事件,因为状态更改不会产生连锁反应,在这种情况下,事件溯源不太可能是一个好方法,并且像 CRUD 这样更简单的方法可能没问题。

    可扩展性

    首先,事件流使一致的写入非常高效 - 它只是一个仅附加的日志,这使得复制和“比较和设置”易于优化。在您需要保护不变量的情况下,像 Cassandra 这样的东西非常慢 - 也就是说,您需要针对“行”的当前状态验证命令,如果行发生更改,则在您有机会之前拒绝更新更新它。您要么需要使用“轻量级事务”来确保一致性,要么每个分区有一个写入器线程,这样您就可以确保在允许更新之前可以根据系统的当前状态成功验证命令。当然,您可以使用其中任何一种方法(单线程/轻量级事务)在 Cassandra 中实现事件存储。

    不过,读取可扩展性是最大的性能优势 - 因为您可以通过从事件流中读取数据来构建任意数量的不同最终一致的投影(视图),并根据需要在这些视图上水平扩展查询服务.这些视图可以根据需要使用自定义数据库(Cassandra、图形数据库),以允许尽可能多地优化查询。它们可以存储非规范化数据,以允许在单个(非连接)数据库查询中获取所有必需的数据。他们甚至可以将投影状态存储在内存中,以获得最佳性能。虽然这可以在没有事件溯源的情况下实现,但实现起来要复杂得多。

    如果您没有复杂的查询和高可扩展性要求,事件溯源可能不是正确的解决方案。

    可进化性

    如果您需要以新的方式查看数据,例如在应用中创建新的客户端应用或屏幕,则可以很容易地将事件流的新投影添加为新的独立服务。如果您需要将一些数据添加到您错过的现有读取视图中,或者修复读取视图中的错误,您可以使用事件流重建视图并丢弃旧视图。与非事件源案例相比,这里的优势是:

    • 您无需编写数据库迁移代码,然后编写代码以在事件进入时使视图保持最新。相反,您只需编写代码以使其保持最新,然后在来自的事件上运行它时间的开始。
    • 与此相关,您可以进行更新,而无需关闭查询服务来进行架构更改 - 相反,只需让旧服务版本针对旧数据库运行,使用新服务版本生成一个新数据库,当它赶上事件流时,只需自动切换然后清理旧服务和数据库,一旦你满意新服务和数据库稳定(请注意,旧服务将同时保持最新状态,如果你需要回滚!)。如果没有事件溯源,这可能很难实现。
    • 如果您需要将任何时间信息添加到您的视图中(例如,上次更新的时间、创建时间),这些信息已经可用并且易于添加,但如果没有事件溯源,则无法追溯添加。

    请注意,以上内容不是关于修改事件流(这很棘手,请参阅下面我对挑战的评论) - 它是关于使用现有事件流来增强视图或创建新视图。

    有一些简单的方法可以在不使用事件溯源的情况下做到这一点,例如使用数据库视图(使用 RDBMS),但它们的可扩展性不高。

    事件溯源在可演化性方面也存在一些挑战 - 您需要处理事件版本控制,可能会结合使用弱事件模式(因此您可以添加具有默认值的属性)和流替换(当您想要做更大的事情时)更改为您的活动)。 Greg Young 正在写一本关于这方面的好书。

    审核

    正如你所说,你对此不感兴趣。

    【讨论】:

    • 最有可能的是,您所指的读取可扩展性称为 CQRS。
    • 但我不完全确定我理解您关于写入可扩展性的论点。想象一个请求带有一些状态改变动作。在我什至允许该操作之前,我确实需要先进行验证 - 一些业务规则规定了这一点!所以我的状态是存储为单行还是事件行的联合并不重要,我需要先读取它,然后再验证和写入,并且没有真正好的方法来确保另一个写入没有t 滑进去
    • 同样,我认为我也不同意 Evolvability 论点。如果我需要将一些数据添加到现有的读/写或任何其他视图中,如何将其添加到单独的行中比修改整个事件流更难?充其量,它可能同样困难。
    • 非常感谢您提供如此详尽的答案,但这似乎是一个相当复杂的问题!
    • 在 Cassandra 中,您可以使用“轻量级事务”确保写入不会在两者之间滑动。它们让您在写入时设置先决条件 - 通常您只需使用 CQL 中的 IF 条件检查“行”的当前版本是否与您加载的内容相匹配 - 这会大大降低 Cassandra 的速度,因为它使用复杂的共识算法(Paxos ) 使其工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-20
    • 2021-02-04
    • 1970-01-01
    • 2011-11-10
    • 2013-06-18
    • 2018-07-09
    • 1970-01-01
    相关资源
    最近更新 更多