在两个日期时间之间存储和查询公告

时间:2020-05-08 14:57:42

标签: amazon-dynamodb dynamodb-queries

背景

我必须设计一个表以在DynamoDB中存储公告。每个公告具有以下结构:

{
    "announcementId": "(For the frontend to identify an announcement to the backend)",
    "author": "(id of author)",
    "displayStartDatetime": "",
    "displayEndDatetime": "",
    "title": "",
    "description": "",
    "image": "(A url to an image)",
    "link": "(A single url to another page)"
}

由于我们仍在设计表格,因此可以更改结构。尤其可以更改announcementIddisplayStartDatetimedisplayEndDatetime

主要访问方式是查找当前公告。用户拥有一个网页,他们可以查看所有当前的公告及其详细信息。

每个公告都有日期,表示何时开始显示(displayStartDatetime)和何时停止显示(displayEndDatetime)。在当前日期时间已过displayEndDatetime之后,公告仍应保留在表格中,以供管理员参考。

开始和结束日期时间精确到分钟。

问题

理想情况下,我想一种方法可以在一个查询中查询表中所有当前的公告。

但是,我得出的结论是,不可能在一个排序键中融合两个日期时间,因为不可能对两个具有相同重要性的数据进行排序(例如,将时间戳存储为字符串将意味着一个将更多)重要/更大)。

因此,作为一种折衷,我想按displayEndDatetime对表值进行排序,以便可以过滤掉过去的公告。这是因为随着时间的流逝,过去的公告将比将来的公告更多,因此对它进行优化将更为有益。

受损的解决方案

目前,我(不是很好)的解决方案是:

  1. 使用一个“热”分区键,并使用displayEndDatetime作为排序键。

这使我可以过滤掉过去的公告,但这也意味着所有数据都在一个分区中。我可以时不时地执行计划的工作,然后将过去的公告移至其他隔开的分区。

  1. Scan浏览表格

我相信Scan在执行任何过滤之前会先查看表中的每个项目。这个解决方案似乎不如1。但是它是最简单的实现,可以让我保留announcementId作为分区键。

  1. Scan表格的GSI

由于Scan将仔细检查每个项目,因此创建GSI(announcementId (PK), displayEndDatetime (SK))并进行扫描以检索所有未通过的announcementId可能会更有效。之后,可以提出另一个要求以获取所有公告。

问题

使用DynamoDB时,用于存储所有公告然后查找当前公告的最优化的解决方案是什么?

尽管我列出了一些对displayEndDatetime进行排序的解决方案,但要点仍然是在开始和结束日期时间之间找到公告。

编辑

以下是@tugberk在后台提出的问题的答案:

  • 您期望接收的写入速率(即您需要处理的每秒峰值写入)是多少?

我不确定管理员将如何使用该系统,公告可能非常定期(大约3个/天)或非常不频繁(大约3个/月)。

  • 您预计每天会存储多少新数据?您如何看待它?

如上所述,这可能是每天大约3条公告或每月3条公告。只要我应该担心,这种情况就可能保持不变。

  • 读取速率(例如每秒峰值读取)是多少?

我希望每秒的峰值读取速度约为500-1000读取/秒。随着更多的用户,这个数字有望增长。

  • 用户一次可以看到多少条公告(即,在任何时间点可见的平均/最大数量的公告)?实际上,这不应超过几个(最多10-20个)。

我希望可见的公告最大数量为30-40。这是因为可能会有多个长期公告以及短期公告。平均而言,我预计会发布5到10条公告。

  • 您在这里希望获得的数据不一致差距是多少(例如,您是否需要秒级精度,或者您愿意在显示和隐藏公告时延迟约1分钟)?

我认为公告开始显示的速度非常重要,特别是如果管理员认为这是紧急公告(可能是紧急的)的良好平台。但是,停止显示的重要性不那么重要,但是为了避免使用户感到困惑,公告应该在显示结束日期时间之后最多4小时停止显示。

1 个答案:

答案 0 :(得分:1)

在这里,这类问题总是很难回答,因为对答案的假设太多了,因为很难掌握所有事实。但是,我将尽力为您提供一些想法,这可能有助于您考虑数据存储的选择以及其他选择。

我知道我在做什么,真的需要使用DynamoDB

根据OP对我的原始问题的答案来编辑此答案。

由于出于内部原因您确实需要DynamoDB,所以我认为将数据存储在两个DynamoDB表中以同时提供读取和写入功能,因为您可以想到的几乎所有访问模式都会对多个分区产生影响有一张桌子。您可以摆脱GSI的束缚,但是如何做到这一点并不太简单,而且我不确定以这种方式进行操作是否有任何优势。

您需要优化的核心内容是读取,因为您提到它可以达到2K / rps,这足以使它成为优化架构所针对的部分。根据您假设每天发布3条公告的假设,就撰写而言,无需担心。

总体思路是这样:

  • 我会考虑使用一个DynamoDB表来处理写操作,在该写操作中,您可以将author标识符配置为分区键,并将announcement标识符配置为排序键(并使主键作为两者的结合)。这将使您可以轻松查询给定作者的所有公告。

  • 我还将有第二个DynamoDB表来处理读取,您将仅存储活动公告,应用程序可以使用Scan查询(即O(N)来查询和检索所有活动公告。 ),这与您提到的无关紧要,因为在任何时间点只会有30-40个主动通知。让我们想象这甚至是500,您仍然可以使用此结构。在分区和排序键方面,我只需要一个active布尔字段作为分区键,就可以将其始终为true,可以将公告ID作为排序键,并且将两者组合作为主键。如果您关心这些公告的排序,则可以相应地调整排序键,但要确保其唯一(即,考虑将公告标识符串联,例如{displayBeginDatetime-in-yyyyMMddHHmmss-format}-{announcementId}。通过这种方式,您将保证只打一个分区但是,您实际上可以简化此操作,并将公告标识符作为分区键和主键,因为我几乎可以肯定DynamoDB将所有数据存储在一个分区中,因为它会很小。不确定100%。此处的要点是,确保通过此查询命中一个分区要好得多。

这是可能的工作方式,在某些情况下我忽略了:

  • 将写入内容记录在第一个DynamoDB中以进行公告。编写公告后,将displayEndDatetime配置为该行的TTL,并假设公告到期时不需要在该表中使用此记录。
  • 有一个作业运行N分钟(一个或多个,取决于您可以处理的数据不一致间隙),这将Scan跨分区划分整个DynamoDB表(以分页方式进行) ),并决定当前可见的公告。然后,将您的数据写入我们上面建立的结构中的第二个DynamoDB表中,该表将处理读取操作,以便您的消费者可以从此表中读取数据,而不必担心任何过滤,因为数据已被过滤(例如,所有公告这是可见的)。请注意,Scan在这里很好,因为您每N分钟运行一次,并假设您可以接受至少1分钟+处理时间数据不一致的间隔。如果您对数据的一致性要求不高,建议每10分钟左右运行一次。
  • 在读取存储系统上,还将displayEndDatetime配置为该行的TTL,以便自动删除该行。
  • Configure DynamoDB streams在第一个DynamoDB表上,该表具有24小时保留和仅一次交付保证,并且具有该流的lambda使用者,可以在删除项目时进行处理(当TTL插入时会发生如您提到的那样,出于更长的保留原因,需要在其他位置保留此公告的记录,并且需要通过不同的访问方式来公开它(例如,按作者显示所有公告,以便他们可以重新启用旧公告)。你问。您可以configure a lambda event sourcing with DynamoDb streams,这将使您能够处理重试等故障。请确保这些lambda中的逻辑是幂等的,以便可以安全地重试。

以下是我原来的问题中的各个部分,这些部分仍然与任何可能尝试实现相同目标的人有关。因此,我将它们留在此处,但它们的相关性不高,因为OP需要使用DynamoDB。

为什么要使用DynamoDB?

首先,我会问为什么要为此需要DynamoDB,因为看起来您的需求读起来比写繁重,所以我认为DynamoDB由于其开箱即用的分区特性而表现得最为出色。

以下问题将帮助您了解您是否真的需要DynamoDB,还是可以摆脱更灵活的数据存储系统:

  • 您期望接收的写入速率是多少(即您需要处理的每秒峰值写入)?
  • 您预计每天会存储多少新数据,并且您认为这会增长吗?
  • 读取速率(例如每秒峰值读取)是多少?
  • 用户一次可以看到多少条公告(即,在任何时间点可见的平均/最大数量的公告)?实际上,这不应超过几个(最多10-20个)。这将帮助您了解您是否可以一口气拉出所有可见的公告,还是需要一个分页系统。
  • 您在这里希望获得的数据不一致差距是多少(例如,您是否需要秒级精度,或者您愿意在显示和隐藏公告时延迟约1分钟)?

实际上,我不需要DynamoDB

基于我对这种用例的消耗和管理员需求的假设,我相信您不需要DynamoDB来进行此假设(假设对此没有大量写入(这可能是错误的)),并且如果这些假设是正确的,那么以上内容对您来说是一个超级解决方案。可以说这是正确的,我认为您最好使用PostgreSQL,这可以使您轻松地更改访问模式,以适应进一步的索引编制;对于当前的访问模式,可以进行范围查询在开始时间和结束时间。