Amazon dynamodb 多对多关系:全局二级索引与邻接列表

Amazon dynamodb 多对多关系:全局二级索引与邻接列表,amazon-dynamodb,adjacency-list,amazon-dynamodb-index,Amazon Dynamodb,Adjacency List,Amazon Dynamodb Index,为了在Dynamo DB中表示多对多关系,我通常看到两种方法:全局二级索引(GSI)和邻接列表。现在我的问题是,什么时候用哪个 GSI的使用基本上是翻转分区键和排序键,以便在这两种情况下都能高效地查询数据。示例显示了与玩家一起玩的在线游戏,例如 Players table -------------- Partition | Sort ----------------- Player 1 | Game 1 Player 1 | Game 2 Player 2 | Game 1 Player

为了在Dynamo DB中表示多对多关系,我通常看到两种方法:全局二级索引(GSI)和邻接列表。现在我的问题是,什么时候用哪个

GSI的使用基本上是翻转分区键和排序键,以便在这两种情况下都能高效地查询数据。示例显示了与玩家一起玩的在线游戏,例如

Players table
--------------
Partition | Sort
-----------------
Player 1  | Game 1
Player 1  | Game 2
Player 2  | Game 1
Player 3  | Game 2

Games GSI
-----------
Partition | Sort
-----------------
Game 1    | Player 2
Game 1    | Player 2
Game 2    | Player 1
Game 2    | Player 3 
我假设这些都是在同一个游戏平台上进行的,也就是说,与有限数量的玩家进行比赛

这一切似乎都是直截了当和合乎逻辑的实施。。。直到数据变得更复杂。如果玩家和游戏都有自己的属性集呢?假设一个游戏在开始时有属性,玩家有用户名和个人游戏分数等属性。如何将这些数据投影到每个表和GSI上

例如,所需的预测如下

让玩家参与游戏

// query made with game id
{
  start_date: '2018-11-04T13:00Z',
  status: 'IN_PROGRESS',
  players: [
    {
      username: 'starkshark',
      points: 200
    },
    {
      username: 'coldshot',
      points 300
    }
  ]
}
获取玩家参与的游戏

// query made with player id
{
  username: 'starkshark',
  games: [
    {
      status: 'IN_PROGRESS',
      start_date: '....'
    },
    {
      status: 'ENDED',
      start_date: '...',
      end_date: '...'
    }
  ]
}
或者,当需要使用邻接列表模式时,这是临界情况吗?从我对邻接列表的总体了解来看,实现简单的多对多关系(如上面的在线游戏示例)似乎相当复杂。据我所知,这意味着要对多个节点相互链接的图进行建模。当然,在这种情况下,节点将是游戏和玩家(可能还有模型中需要的任何其他实体)


TLDR:最后一个问题是,当实体之间有自己的属性集时,邻接列表是要寻找的选项还是模型的数据结构不太复杂?

当实体有自己的属性集时,您可以在邻接列表中将其建模为循环回自身的边

Id1       | Id2     | Data
- - - - - - - - - - - - - - - - - - - 
User1     | User1   | {email=...}
或者,可以将属性建模为从实体到属性类型的边

Id1       | Id2     | Data
- - - - - - - - - - - - - - - - - - - 
User1     | Email   | bob@...
这完全取决于您希望如何查询数据。第二种方法的优点是,您可以使用GSI,其中
Id2
是分区键,
Data
是范围键,这将允许您高效地查找与某个属性值匹配的所有用户


对于GSI,邻接模式通常有一个GSI,其中包含与表相同的主键属性,但顺序已转换,因此您可以方便地在任意方向查询邻接列表。

当实体有自己的属性集时,您可以在邻接列表中将其建模为循环回自身的边

Id1       | Id2     | Data
- - - - - - - - - - - - - - - - - - - 
User1     | User1   | {email=...}
或者,可以将属性建模为从实体到属性类型的边

Id1       | Id2     | Data
- - - - - - - - - - - - - - - - - - - 
User1     | Email   | bob@...
这完全取决于您希望如何查询数据。第二种方法的优点是,您可以使用GSI,其中
Id2
是分区键,
Data
是范围键,这将允许您高效地查找与某个属性值匹配的所有用户


对于GSI,邻接模式通常有一个GSI,其中包含与表相同的主键属性,但顺序已转换,因此您可以方便地在任意方向查询邻接列表。

谢谢您的回答。不幸的是,这并不能真正回答问题。尽管这个问题一开始可能表述得很糟糕。你说“当一个实体有它自己的属性集时,你可以在你的邻接列表中对它建模…”(我的重点)。还有其他选择吗?我必须将其建模为邻接列表还是可以使用更简单的形式?当项目具有属性时,GSI本身是否可以使用?当您具有多对多关系时,您应该使用邻接列表。如果您知道关系的数量是有界的(比如0..5到0..5的关系),那么您可以使用不同的方法对其进行建模。但是,为了双向查询玩家-游戏关系,需要更新游戏对象中的玩家和玩家对象的游戏,因此没有原子性保证。邻接列表模式并不是那么复杂,它只需要一次写入就可以将玩家添加到游戏中,所以你不必担心它是酸性的。“它只需要一次写入就可以将玩家添加到游戏中,所以你不必担心它是酸性的”。你的意思是只添加新项目?AFAIK,DynamoDB的邻接列表将包含非规范化数据(例如,每个游戏的玩家详细信息),并且必须对多个项目进行更新,其中每个写入都是原子的,但整个过程中不存在事务范围(更新多个项目)。这是通过NoSQL设计的,并且可以容忍,因为与读取相比,更新预计是罕见的。我的意思是,如果您能够以某种方式对数据建模,您可能只需要写操作就可以完成某些任务。(我承认我的术语有点草率。)使用邻接列表,只需一次写入即可将玩家添加到游戏中,因为您只需在数据库中写入新玩家游戏行。如果没有邻接列表,则需要将玩家添加到游戏项中,并且需要将游戏添加到玩家项中。每个写入操作在项目级别都是原子的,因此写入单个项目是原子的,但更新两个项目不是原子的。是。谢谢你的澄清。谢谢你的时间和耐心,非常感谢:)谢谢你的回答。不幸的是,这并不能真正回答问题。尽管这个问题一开始可能表述得很糟糕。你说“当一个实体有它自己的属性集时,你可以在你的邻接列表中对它建模…”(我的重点)。还有其他选择吗?我必须将其建模为邻接列表还是可以