多对多关系:全局二级索引与邻接列表
Many-to-many relationship: Global Secondary Index vs. Adjacency list
为了在 Dynamo DB 中表示多对多关系,我经常看到两种方法:全局二级索引 (GSI) 和邻接列表。现在我的问题是,什么时候使用哪一个?
GSI的使用基本上就是把partition key和sort key翻转过来,这样两种情况下都可以高效的查询数据。示例显示了与玩家在线游戏之类的内容,例如
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
我假设这些都是同一游戏平台内的所有会话,即与有限数量的玩家进行比赛。
这一切看起来都很简单,而且实施起来合乎逻辑……直到数据变得有点复杂。如果 Players 和 Games 都有自己的一组属性怎么办?假设一个游戏具有开始时间的属性,而玩家具有用户名和个人游戏分数等属性。如何将这些投影到 Table 和 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,它包含与 table 相同的主键属性,但顺序调换,以便您可以轻松地在任一方向查询邻接列表。
为了在 Dynamo DB 中表示多对多关系,我经常看到两种方法:全局二级索引 (GSI) 和邻接列表。现在我的问题是,什么时候使用哪一个?
GSI的使用基本上就是把partition key和sort key翻转过来,这样两种情况下都可以高效的查询数据。示例显示了与玩家在线游戏之类的内容,例如
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
我假设这些都是同一游戏平台内的所有会话,即与有限数量的玩家进行比赛。
这一切看起来都很简单,而且实施起来合乎逻辑……直到数据变得有点复杂。如果 Players 和 Games 都有自己的一组属性怎么办?假设一个游戏具有开始时间的属性,而玩家具有用户名和个人游戏分数等属性。如何将这些投影到 Table 和 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,它包含与 table 相同的主键属性,但顺序调换,以便您可以轻松地在任一方向查询邻接列表。