Cassandra中行动态分组的数据建模

yrdbyhpb  于 2023-03-08  发布在  Cassandra
关注(0)|答案(1)|浏览(233)

我有一个游戏事件的提要,其模式如下所示;

{
   "gameId": text,
   "playerId": text,
   "eventId": text,
   "ts": bigint,
   "metrics": map<text,double>,
   "dimensions": map<text, text>
}

示例事件可能如下所示;

{
   "gameId": "eb9dafbf-d81a-4d2a-b19b-f9149ee90520",
   "playerId": "738a90ef-f09a-459b-91af-452f25f48c8d",
   "eventId": "ebec5e8c-118b-42a2-aa87-2ecdbc42aa58",
   "ts": 1677685878,
   "metrics": {
      "moves": 11.0
      "xp": 100.0,
      "hp": 431.0
   },
   "dimensions": {
      "characterId": "5fdf53f0-ad6d-43fc-a422-8a679277f4df",
      "serverId": "d07e57c2-166a-4119-8d4b-cc0778d10069",
      "tournamentId": "40e53993-46b0-42d8-be5a-4fd199a31ad3"
   }
}

表主键可能是gameId, playerId, day, (ts, eventId)。Day是从ts值中提取的YYYY-mm-DD,以保持分区大小较小,并允许高性能的插入。
我需要通过dimensions的动态集合查询这些事件,实际上是根据提供的维度键的值将所有事件分配到分组中。
在关系环境中,用于执行此操作的伪SQL可能类似于;

WITH 
segments(seg_ordinal, seg_character, seg_tournament) AS (
  VALUES (1, 'char_1', 'tourn_1'), (2, 'char_1', null), (3, null, 'tourn_1'), (4, null, null)
),
exploded(gameId, playerId, eventId, dimensions, seg_ordinal, seg_character, seg_tournament) AS (
    SELECT *
    FROM events e, segments s
    WHERE (e.dimensions.characterId = s.seg_character OR s.seg_character IS null)
    AND   (e.dimensions.tournamentId = s.seg_tournament OR s.seg_tournament IS null)
)
SELECT e1.seg_ordinal AS Segment_Ordinal, SUM(e1.metrics.xp) AS AggregatedResult
FROM exploded e1
LEFT JOIN exploded e2 ON e1.eventId = e2.eventId AND e2.seg_ordinal < e1.seg_ordinal
WHERE e2.eventId IS NULL
GROUP BY e1.seg_ordinal
ORDER BY e1.seg_ordinal

由于维度键(在本例中为characterIdtournamentId)和要聚合的指标(在本例中为SUM(xp))是动态的,并且在查询之前并不总是已知的,因此很难将它们扁平化为适合Cassandra表的模式。
维Map条目上的辅助索引将允许我按任何维进行查询,如果我需要同时按多个维进行查询,如所提供的示例中所示,我需要允许对查询进行过滤。这可能是可以的,因为将提供所有分区键,因此所有过滤仍将限于单个Cassandra分区。当查询需要扫描多个分区时,二级索引通常是麻烦的,然而这仍然不能解决“分段”需求的动态特性。
我应该如何最好地建模,以适合在Cassandra中查询?

eanckbw9

eanckbw91#

您已经很好地理解了Cassandra数据建模规则。首先,有几个想法值得一提,也许可以改进设计:

    • 改进**
  • 在cassandra列名中使用snake大小写
  • gameIdplayerId可以是uuid类型(根据示例中它们的格式)
  • tseventid可以是timeuuid类型的单个字段(精度为100ns),并且易于使用now()插入
  • 在Cassandra表中添加列几乎是免费的,因为如果为空,它不会添加任何空值。那么为什么不将每个维都作为一个适当的列呢?将所有维都放在Map中,您可能会尝试冻结值,如果您频繁编辑它们,这将创建相当多的墓碑。
  • 如果在where子句中你发现了一个特定的分区(提供了3个部分),那么创建一个辅助索引就没有用了。你可以简单地在那里(并且只在那里)使用ALLOWFILTERING关键字。
    • 第一个建议是:**
CREATE TABLE IF NOT EXISTS games (
  game_id      uuid,
  player_id    uuid,
  day_yyymmdd  text,
  event_id     timeuuid,
  metrics      map<text,double>,
  dim_character_id  uuid,
  dim_server_id     uuid,
  dim_tournament_id uuid,
  PRIMARY KEY((game_id, player_id, day_yyymmdd), event_id)
) WITH CLUSTERING ORDER BY (event_id desc);

现在关于动态查询部分:
即使您基于不同的查询在多个表中复制数据,但它完全是动态的,而且聚合片使它成为一个死胡同。
"你该怎么做"
查询整个分区并在代码级别进行聚合

相关问题