我有一个游戏事件的提要,其模式如下所示;
{
"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
由于维度键(在本例中为characterId
和tournamentId
)和要聚合的指标(在本例中为SUM(xp)
)是动态的,并且在查询之前并不总是已知的,因此很难将它们扁平化为适合Cassandra表的模式。
维Map条目上的辅助索引将允许我按任何维进行查询,如果我需要同时按多个维进行查询,如所提供的示例中所示,我需要允许对查询进行过滤。这可能是可以的,因为将提供所有分区键,因此所有过滤仍将限于单个Cassandra分区。当查询需要扫描多个分区时,二级索引通常是麻烦的,然而这仍然不能解决“分段”需求的动态特性。
我应该如何最好地建模,以适合在Cassandra中查询?
1条答案
按热度按时间eanckbw91#
您已经很好地理解了Cassandra数据建模规则。首先,有几个想法值得一提,也许可以改进设计:
gameId
,playerId
可以是uuid
类型(根据示例中它们的格式)ts
和eventid
可以是timeuuid
类型的单个字段(精度为100ns),并且易于使用now()
插入现在关于动态查询部分:
即使您基于不同的查询在多个表中复制数据,但它完全是动态的,而且聚合片使它成为一个死胡同。
"你该怎么做"
查询整个分区并在代码级别进行聚合