使用Cassandra作为事件存储

kh212irz  于 4个月前  发布在  Cassandra
关注(0)|答案(7)|浏览(49)

我想尝试在事件源应用程序中使用Cassandra作为事件存储。我对事件存储的要求非常简单。事件'schema'类似于以下内容:

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

我对Cassandra完全陌生,所以请原谅我对即将写的内容的无知。我只想对这些数据运行两个查询。
1.给予给定聚合根ID的所有事件
1.如果序列号> x,则给予给定聚合根的所有事件
我的想法是在CQL中创建一个Cassandra表,如下所示:

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

字符串
这看起来像是一个合理的建模问题的方法吗?而且,重要的是,使用复合主键是否允许我有效地执行我指定的查询?请记住,给定用例,对于相同的聚合根ID可能有大量的事件(具有不同的seq_num)。
我特别担心的是,第二个查询在某种程度上会效率低下(我在这里考虑的是二级索引……)

cetgtptt

cetgtptt1#

您的设计似乎在“Cassandra术语”中很好地建模了。您需要的查询确实在“组合键”表中得到了支持,您将得到类似于以下内容的结果:

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

我不认为第二个查询会是低效的,但是它可能返回很多元素...如果是这样的话,你可以设置一个要返回的事件的“限制”。如果是这样的话,你可以使用limit关键字。
使用组合键似乎很适合您的特定需求。使用“二级索引”似乎并没有给表带来太多...除非我在您的设计/需求中遗漏了一些东西。
HTH的。

vmdwslir

vmdwslir2#

你已经得到的是好的,除了在一个特定的聚合的许多事件的情况下。你可以做的一件事是创建一个静态列来保存“next”和“max_sequence”。这个想法是静态列将保存这个分区的当前max序列,以及下一个分区的“artificial id”。然后,比如说,每个分区存储100或1000个事件。这样做的实质是将聚合的事件分桶到多个分区中。这意味着查询和存储的额外开销,但同时防止无限制的增长。您甚至可以为聚合创建分区查找。这真的取决于您的用例以及如何“聪明”你希望它是。

bzzcjhmw

bzzcjhmw3#

我一直在使用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;

字符串

zzwlnbp8

zzwlnbp84#

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

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

字符串
这样,你的id将成为一个集群列,只是为了保证事件的唯一性,你的分区键(即20160922)可以分组每天的所有事件。你也可以将它改为月。避免使用uuid使用timeuuid,它已经存储了时间戳信息。

9q78igpj

9q78igpj5#

这个设计似乎与Cassandra存储数据的方式一致,即主键的第一部分,即您的“id”将用于在单独的节点/v节点上划分数据(取决于你的集群是如何配置的),这将使Cassandra很容易为你的第一个查询获取数据,因为它只需要触及一个分区,现在,按照键的第二部分,is将是一个集群键,即is将指定数据在该分区内的排序方式,这就是你的第二个查询的全部内容。记住,只要你所有的数据都是以这样的方式设计的,即表上的每个查询只涉及单个分区,如果你担心第二个查询会返回大量的数据,你可以选择Cassandra为范围查询提供的分页。

bqujaahr

bqujaahr6#

  • 10年后才给予我的输入,但是我也在使用cassandra构建事件存储,并希望分享我的输入。*

我相信将主键改为((id), seq_num)而不是(id, seq_num)可能会有好处,因为这一更改会显著影响Cassandra中的数据分发和访问模式。
使用(id, seq_num)idseq_num共同决定分区,这可能导致数据分布效率降低,特别是当seq_num数量很大或高度可变时。这可能导致宽分区并影响性能。另一方面,使用((id), seq_num)明确分离分区和集群责任:id成为唯一的分区键,确保单个流的所有事件存储在一起,而seq_num用作集群键,维护每个流/聚合分区内的事件顺序。
这种结构对于事件源更具有可扩展性和效率,因为它通过与以顺序方式访问特定流中的所有事件的常见模式保持一致来优化数据存储和检索。

vsikbqxv

vsikbqxv7#

我不同意你在www.example.com上保存aggregateroot的设计,应该eventstore.you保存domainevent以获得灵活性。我解释说,eventdomain是最小粒度的数据,它可以改变应用程序的状态。aggregateroot与eventstore不匹配,它是用于数据交换或有界上下文。当你使用域事件时,你可以用plolygot建模来重建你的数据,甚至aggregateroot。你可以根据客户端和约束的需要来管理模型。所以你可以为应用程序之间的链接建模图形domainobject和之后你使用neo4j,另外你建模聚合模型和你使用documentdatabase.我的意思是你有能力改变模型和使用方便的持久化engine.it是polygot数据和polygot持久化之间的区别.在你的策略中我理解两种方式:如果你需要eventsourcing,你可以在domainevent和cassandra数据库上建模。如果你需要aggregateroot数据或模型而没有eventsourcing,你可以使用文档化的数据库,你可以检索这两个查询。
你应该消除对领域驱动设计的困惑。

相关问题