17

我想尝试在事件源应用程序中使用 Cassandra 作为事件存储。我对活动商店的要求非常简单。事件“模式”将是这样的:

  • id:聚合根实体的 id
  • data : 序列化的事件数据(例如 JSON)
  • 时间戳:事件发生的时间
  • sequence_number:事件的唯一版本

我对 Cassandra 完全陌生,所以请原谅我对即将写的内容一无所知。我只有两个想要在这些数据上运行的查询。

  1. 给我给定聚合根 ID 的所有事件
  2. 如果序列号 > x,给我给定聚合根的所有事件

我的想法是在 CQL 中创建一个 Cassandra 表,如下所示:

CREATE TABLE events (
  id uuid,
  seq_num int,
  data text,
  timestamp timestamp,
  PRIMARY KEY  (id, seq_num) );

这似乎是对问题建模的明智方法吗?而且,重要的是,使用复合主键是否可以让我有效地执行我指定的查询?请记住,在给定用例的情况下,同一聚合根 ID 可能存在大量事件(具有不同的 seq_num)。

我特别担心的是第二个查询在某种程度上会效率低下(我在这里考虑二级索引......)

4

6 回答 6

8

您的设计似乎在“cassandra 术语”中得到了很好的建模。“复合键”表确实支持您需要的查询,您将拥有如下内容:

  • 查询1 select * from events where id = 'id_event':;
  • 查询 2: select * from events where id = 'id_event' and seq_num > NUMBER;

我认为第二个查询不会效率低下,但是它可能会返回很多元素……如果是这种情况,您可以设置要返回的事件的“限制”。如果可能,您可以使用limit关键字。

使用复合键似乎很适合您的特定要求。使用“二级索引”似乎并没有带来太多好处……除非我错过了您的设计/要求中的某些内容。

HTH。

于 2013-10-11T15:57:12.443 回答
2

我一直在将 Cassandra 用于非常相似的场景(每行 100k+ 列),并以接近您的模型结束。我也同意 emgsilva 的观点,即二级索引可能不会带来太多。

事实证明,对于我们的事件存储的良好性能来说,有三件事很重要:使用复合列,确保列的排序顺序很好(Cassandra 按行按列对数据进行排序),并尽可能使用紧凑存储。

请注意,紧凑存储意味着您只能拥有一个值列。因此,您需要使所有其他列成为键的一部分。

对您来说,架构将是:

CREATE TABLE events (
    id uuid,
    seq_num int,
    timestamp timestamp,
    data text,
    PRIMARY KEY  (id, seq_num, timestamp))
    WITH COMPACT STORAGE;
于 2015-04-27T09:51:48.917 回答
2

您所拥有的一切都很好,除非针对特定聚合有许多事件。您可以做的一件事是创建一个静态列来保存“next”和“max_sequence”。想法是静态列将保存此分区的当前最大序列,以及下一个分区的“人工 id”。例如,您可以在每个分区存储 100 或 1000 个事件。您基本上所做的就是将聚合的事件存储到多个分区中。这将意味着查询和存储的额外开销,但同时可以防止无限增长。您甚至可以为聚合创建分区查找。真的取决于您的用例以及您希望它有多“聪明”。

于 2015-03-05T13:12:35.030 回答
0

该设计似乎与 Cassandra 存储数据的方式一致,主键的第一部分,即您的“id”将用于在单独的节点/v-nodes 上分区数据(取决于您的集群的配置方式) ,这将使 Cassandra 非常容易为您的第一个查询获取数据,因为它只需要触及一个分区,现在根据您的键的第二部分将是一个集群键,即指定数据在其中的排序方式分区,这就是您的第二个查询的全部内容。请记住,只要您的所有数据都以这样一种方式设计,即对表的每个查询只涉及一个分区,那么您就可以开始了。此外,如果您担心第二个查询将返回大量数据,您始终可以选择 Cassandra 固有地为范围查询提供的分页。

于 2019-03-03T11:06:11.480 回答
0

您的分区键太细,您应该创建一个复合分区键或更改它以获得更好的时间序列建模性能。例如

CREATE TABLE events (
    event_date int,
    id timeuuid,
    seq_num int,
    data text,
    PRIMARY KEY  (event_date, id) );

这样,您的 id 将成为一个集群列,以保证事件的唯一性,并且您的分区键(即 20160922)可以每天对所有事件进行分组。您也可以将其更改为月份。避免使用 uuid 使用 timeuuid 代替,它已经存储了时间戳信息。

于 2016-09-22T08:10:57.300 回答
-6

我不同意您将聚合根保存在 eventstore 上的设计。您应该保存 domainevent 以获得灵活性。我解释 eventdomain 是最细粒度的数据,它使 application.aggregateroot 与 eventstore 的状态不匹配,它用于数据交换或 boundedcontext。当您使用域事件时,您甚至可以使用 plolygot 建模来重建数据甚至聚合根。您可以根据客户和约束的需要来管理模型。因此,您可以为域对象之间的链接建模图形,然后使用 neo4j,此外还可以建模聚合模型和您使用文档数据库。我的意思是您有机会更改模型并使用方便的持久性引擎。它是 polygot 数据和 polygot 持久性之间的区别。在你的策略中,我理解两种方式:如果您需要事件外包,您可以在 domainevent 和 cassandra 数据库上建模。如果您需要聚合根数据或模型并且没有事件源,则可以使用文档化数据库,并且可以检索这两个查询。

您应该消除对领域驱动设计的困惑。

于 2015-03-05T11:21:42.580 回答