Amazon dynamodb 如何在DynamoDb中设计基于likes的推荐系统

Amazon dynamodb 如何在DynamoDb中设计基于likes的推荐系统,amazon-dynamodb,dynamodb-queries,Amazon Dynamodb,Dynamodb Queries,考虑到性能的主要重要性,为了在DynamoDb中构建推荐系统,最好的设计方法是什么 系统需要存储一个url,以及该主题被“喜欢”的次数,但我的要求包括每天、每周、每月和每年的搜索,例如: Give me the top 10 of the week Give me the top 10 of the month 我正在考虑包含日期和时间信息,以便查询可以通过此字段控制它,但不确定它在性能方面是否良好。如果您仅有的数据结构是哈希映射,您将如何解决此问题 如果在这个限制的基础上,您只能每秒更新10

考虑到性能的主要重要性,为了在DynamoDb中构建推荐系统,最好的设计方法是什么

系统需要存储一个url,以及该主题被“喜欢”的次数,但我的要求包括每天、每周、每月和每年的搜索,例如:

Give me the top 10 of the week
Give me the top 10 of the month

我正在考虑包含日期和时间信息,以便查询可以通过此字段控制它,但不确定它在性能方面是否良好。

如果您仅有的数据结构是哈希映射,您将如何解决此问题

如果在这个限制的基础上,您只能每秒更新1000次任何键,每秒读取3000次该怎么办

您希望您的商品多久会被喜欢一次?想必会有一些人很受欢迎,很受欢迎,而其他人几乎永远不会得到任何喜欢

您的系统需要多大的实时性?系统最终是否能够保持一致(也就是说,如果您仅在几分钟前报告了喜欢的内容,是否可以)


让我们试一试
免责声明:这在很大程度上是一个说教式的练习——实际上,您可能希望探索一个分析产品,或者一些其他技术,而不是DynamoDB来完成这项任务


第一部分。表示项目并更新类似计数

首先,让我们谈谈你的聚合/分析目标:你提到你想查询“本周前十名”或“本月前十名”,但你没有指定这是指“日历周”/“日历月”还是“过去7天”/“过去30天”

我将从字面上理解它,并假设“本周前十名”是指本周从最近的星期一开始的前十项(如果是星期天的话)。同月:“本月前十名”是指“本月初以来的前十项”

在这种情况下,您可能需要为每个项目存储:

  • 总喜欢数
  • 自本月初以来的喜欢次数
  • 自本周开始的喜欢次数
  • 当前月数-需要确定是否需要重置
  • 当前周数-需要确定是否需要重置
每周重置当前周的计数;每月重置当前月的计数

在DynamoDB中,这可能表示为:

{
   id: "<item-id>",
   likes_all: <numeric>,   // total likes of all time
   likes_wk: <numeric>,    // total likes for the current week
   likes_mo: <numeric>,    // total likes for the current month
   curr_wk: <numeric>,     // number of the current week of year, eg. 27
   curr_mo: <numeric>,     // number of the current month of year, eg. 6
}
这将更新总计数并返回项目的状态。根据curr_wk和curr_mo的值,您必须决定更新的外观。您可以是递增的,也可以设置一个绝对值。假设我们在一周后执行更新,而不是一个月。以及假设上述更新的结果如下所示:

{
   id: "<item-id>",
   likes_all: 1000,  // total likes of all time
   likes_wk: 70,     // total likes for the current week
   likes_mo: 150,    // total likes for the current month
   curr_wk: 26,      // number of the week of last update
   curr_mo: 6,       // number of the month of year of last update
}
dynamodb update-item \
    --table-name <your-table-name> \
    --key '{"id":{"S":"<item-id>"}}' \
    --update-expression "SET curr_wk = 27, likes_wk = :lc, likes_mo = likes_mo + :lc" \
    --condition-expression "curr_wk = :wk AND curr_mo = :mo" \
    --expression-attribute-values '{":lc": {"N":"1"}, ":wk": {"N":"26"}, ":lc": {"N":"6"},}' \
    --return-values ALL_NEW
如果同时发生两个冲突的更新,可以确保不会两次重置LIKE。在这种情况下,其中一个更新将失败,您必须将更新切换回增量


第2部分-跟踪统计数据

要处理好你的统计数据,你需要每周和每月跟踪最喜欢的人

你可以保存每周和每月最热门物品的分类列表。你也可以将这些列表存储在Dynamo中

例如,假设您希望跟踪前3名。您可以存储以下内容:

{
   id: "item-stats",
   week_top: ["item3:4000", "item2:2000", "item9:700"],
   month_top: ["item2:100000", "item4:50000", "item3:12000"],
   curr_wk: 26,
   curr_mo: 6,
   sequence: <optimistic-lock-token>
}
{
id:“物品统计”,
周顶部:[“项目3:4000”、“项目2:2000”、“项目9:700”],
月份顶部:[“项目2:100000”、“项目4:50000”、“项目3:12000”],
当前工作时间:26,
货币:6,
顺序:
}
无论何时对项目执行更新,都会更新统计信息

更新统计数据的算法与更新项目类似,只是不能只使用更新表达式。相反,必须使用GetItem、PutItem和来实现自己的读-修改-写序列

首先,您读取
项目统计数据
特殊项目的当前值,包括当前
序列
的值(这对于检测碰撞很重要)

然后,您确定刚刚更新计数的项目是否会进入前N名每周或每月列表。如果是这样,您将更新
周顶
和/或
月顶
属性,并准备有条件的PutItem请求

PutItem请求必须包括一个条件检查,以验证
项目统计数据的
顺序
是否与您之前读取的内容相同。如果不相同,则需要再次读取该项目并重新计算前N个列表,然后再次尝试放置

此外,与重置项目计数的方式类似,当发生更新时,您需要检查并查看是否需要重置每周或每月顶部,作为更新的一部分

发出PutItem请求时,请确保生成一个新的
序列


第3部分-将所有内容组合在一起

在第1部分和第2部分中,我们了解了如何跟踪喜好和静态信息,但我们的方法存在很大的问题:在任何实际规模下,性能都会非常差;热门项目会给我们带来问题;更新前N个统计信息将是一个重大的瓶颈

为了提高性能并实现一定的可伸缩性,我们不需要更新每个项目以及每个“like”的项目统计信息

我们可以使用队列+dynamodb+计算资源的组合来实现性能和可伸缩性的良好平衡

  • 创建一个队列来存储挂起的类
  • let“likes API”将使用like标记帖子的消息排队,而不是在帖子出现时应用它们
  • 实现队列使用者(可以是Lambda,
    dynamodb update-item \
        --table-name <your-table-name> \
        --key '{"id":{"S":"<item-id>"}}' \
        --update-expression "SET curr_wk = 27, likes_wk = :lc, likes_mo = likes_mo + :lc" \
        --condition-expression "curr_wk = :wk AND curr_mo = :mo" \
        --expression-attribute-values '{":lc": {"N":"1"}, ":wk": {"N":"26"}, ":lc": {"N":"6"},}' \
        --return-values ALL_NEW
    
    {
       id: "item-stats",
       week_top: ["item3:4000", "item2:2000", "item9:700"],
       month_top: ["item2:100000", "item4:50000", "item3:12000"],
       curr_wk: 26,
       curr_mo: 6,
       sequence: <optimistic-lock-token>
    }