包含数千个参考项目的多对多

时间:2018-05-06 06:37:00

标签: nosql amazon-dynamodb

我目前有一个SQL Server数据库,其中包含一个包含400,000部电影的表。我有另一个包含数千名用户的表。

CREATE TABLE [movie].[Header]
(
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [SourceId] [int] NOT NULL,
    [ReleaseDate] [Date] NOT NULL,
    [Title] [nvarchar](500) NOT NULL
)

CREATE TABLE [account].[Registration]
(
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Username] [varchar](50) NOT NULL,
    [PasswordHash] [varchar](1000) NOT NULL,
    [Email] [varchar](100) NOT NULL,
    [CreatedAt] [datetime] NOT NULL,
    [UpdatedAt] [datetime] NOT NULL
)

CREATE TABLE [movie].[Likes] 
(
    [Id] [uniqueidentifier] NOT NULL,
    [HeaderId] [int] NOT NULL,
    [UserId] [int] NOT NULL,
    [CreatedAt] [datetime] NOT NULL
)

CREATE TABLE [movie].[Dislikes]
(
    [Id] [uniqueidentifier] NOT NULL,
    [HeaderId] [int] NOT NULL,
    [UserId] [int] NOT NULL,
    [CreatedAt] [datetime] NOT NULL
)

每位用户都会看到从未来两周开始的100部电影。然后他们可以执行诸如喜欢,不喜欢,推荐等行为。

我正在将整个应用程序移动到无服务器架构中。我通过Lambda + API Gateway在AWS中运行API,现在我正在考虑将DynamoDB用于数据库。我不认为我有任何超级疯狂的东西会阻止我将数据存储在Dynamo中,而他们的定价/消费模型似乎比SQL Server(目前在Azure中托管)便宜得多。

我遇到问题的一件事是理解我如何为在电影上执行操作的用户建模。如果他们喜欢"喜欢"一部电影,它会进入喜欢的列表,他们可以回去看看。在那里,我向他们展示了整个移动记录(实际上包括更多数据,如演员/工作人员/等级等等。我只是截断了电缆以简化它)。如果我存储每个"喜欢"作为Dynamo中的项目,以及整个电影作为属性,我认为用户文档会变得非常大。

我还需要继续向用户展示电影,从两周开始,他们没有对其进行任何操作。他们已执行操作的电影我需要从查询中删除。今天我只是加入电影表和用户操作表,从用户操作表中已存在的查询中删除电影。我将如何使用相同的最终结果在NoSql中对此进行建模?

我可以将喜欢/不喜欢合并到具有动作类型属性(表示喜欢/不喜欢等)的单个文档中,以及已执行动作的电影数组。我还不确定如何过滤[Header]查询,以便用户文档中的电影不会再回来。

我想我会将我的电影哈希键设置为分片的发布日期,因为平均每个发布日期大约有10部电影。这给了一个很好的分布。我认为我使用userid具有包含用户已执行操作的所有电影的文档的哈希键;不确定这是否是正确的道路。

我从未处理过NoSql,所以我想要求输入。我不确定如何最好地设计基本上是一对多的东西,但是每个用户的电影有可能成千上万。

1 个答案:

答案 0 :(得分:2)

所以,根据你的意见,我会提出一个建议。这并不意味着它是一个正确的答案,我也可能错了或错过了一点

首先,请一遍又一遍地阅读Best Practices的每个部分。有些模式你可能从未想过,但仍然可以使用NoSQL方法。它非常有帮助和教育(考虑到你说你是NoSQL的新手)。与您的案例有相似之处,您可以根据最佳实践创建自己的答案。

我可以建议:

NoSQL在查询“不存在”方面非常糟糕。 NoSQL的一个重要技巧是它确切地知道在哪里可以找到您正在寻找的数据,而不是找不到的地方。所以很难找到那些没有对电影执行任何动作的用户。如果您可以使用像Redis这样的侧面数据库,您可以非常轻松地将其关闭。使用Redis数据结构,您可以查询哪些用户尚未喜欢/不喜欢,并从DynamoDB获取其余的电影数据。但是将side数据库Redis暂时放在一边,只使用DynamoDB方法。

一种方法可能是当每部电影到达DB(新电影)时,您可以将其添加到具有操作类型not-actioned-yet的每个用户。现在,对于所有用户,您可以非常轻松快速地查询这些内容。 (现在它知道数据的位置;))但这是不对的,因为如果有10.000个用户,那么每部电影就会产生10.000次写入。

另一种方法可能是假设您在表上有项目,其中包含用户上次“获取尚未执行的操作”列表的日期。现在,经过一段时间后,用户回来查看相同的查询,现在您需要阅读该日期并获取在该日期之后添加到您的数据库中的所有电影。使用日期时间作为排序键,您可以从该日期开始查询电影。可以说,在用户上次查询后添加了10部电影(这些肯定是用户尚未采取行动)。现在,您将这10部电影作为项目not-actioned-yet添加到表格中。在此之后,您将拥有用户尚未采取行动的所有电影。 'not-actioned-yet'也类似'喜欢,不喜欢'。从现在开始,您可以轻松查询它们。

示例表格结构:

您可以使用sparse indexestime series table approach将新电影(接下来的两周内)与其他电影分开。这样,您只能有效地查询或扫描它们。在这里使用稀疏索引

电影表

| Id (Hash Key|Primary Key) | StartingDateUnix(GSI SK) | IsIn2Weeks (GSI) |
|:-------------------------:|-------------------------:|:----------------:|
| MovieId1                  |        1234567           |     1     
| MovieId2                  |        1234568           |     1    
| MovieId3                  |        001123            |     null     

要在unix 1234567之后获取电影,您必须使用大于unix时间的排序键来查询GSI。

用户操作表

| UserId (Hash Key) | ActionType_ForMovie(Sort Key) | CreatedAt (LSI) |
|:-----------------:|:-----------------------------:|:---------------:|
| UserId1           |       no-action::MovieId1     |      1234567    |
| UserId1           |       no-action::MovieId2     |      1234568    |   
| UserId1           |       like::MovieId3          |      1234569    | 
| UserId1           |       like::MovieId4          |      1234561    |     
| UserId1           |       dislike::MovieId5       |      1234562    |   

使用排序键可以查询所有不喜欢尚未操作的喜欢...并且您可以按日期对它们进行排序。你也可以分页。

我花了一些时间来解决这个问题,因为它对我来说也是一个很好的挑战,我希望得到一些反馈。希望它在某种程度上有所帮助