DynamoDB架构,用于按属性过滤用户

时间:2015-04-27 10:55:15

标签: database-schema amazon-dynamodb

我们正在寻找替换我们当前的RDBMS数据库,并且一直在考虑一些替代方案。我们有大量的时间序列信息,我们对如何在没有问题的Dynamo数据库中进行表示非常了解。

我们目前在下表中存储每个人的人物属性:

people (id, name, email, phone)
people_attributes (id, person_id, attribute_name, attribute_value)
people_location (id, person_id, location_id) (links to locations table)
people_devices (id, person_id, device_id)
people_metrics (id, person_id, metric_type, value) -- very very large table

如下所示的查询将最佳方式表示为Dynamo数据库架构的最佳方式是什么:

Get all people that  ( 
    live in Moscow 
    OR  
    live in Athens 
    OR  
    live in Istanbul 
    OR 
    live in San Francisco ) 
AND 
have an iPhone 
AND ( EITHER
          have at least one metric of type X
          OR
          have at least one metric of type Y )

1 个答案:

答案 0 :(得分:2)

让我们先看看我们应该首先避免的事情,这样我们就可以缩小实施的选项范围:

  • 我们应该避免表扫描(只要有可能),主键查询总是最好的方法

  • 我们应该避免不均匀的访问模式,选择包含未均匀访问的值的哈希密钥将无法充分利用您的预配置吞吐量

  • 我们应该通过让返回少量记录的查询而不是返回大量数据的单个查询来避免突发读取活动

这是我的建议:

我们应该从MOST RESTRICTIVE查询开始,尽快从数据集中排除最大记录数。

为了实现上述某些指导原则,我们可能需要对数据模型进行非规范化,以便在下表中包含一些属性:

people (id, name, email, phone, device_id, location_name)

您可以按如下方式创建全球二级索引

Hash Key  : location_name
Range Key : device_id

*旧的哈希键(id)将自动投影到索引中,并且应该是查询返回的唯一属性。

因此,第一个查询将解决您需求的前两部分:

Get all people that  ( 
    live in Moscow 
    OR  
    live in Athens 
    OR  
    live in Istanbul 
    OR 
    live in San Francisco ) 
AND 
have an iPhone

通过对每个位置进行一次查询,您可以为DynamoDB提供平衡查询执行的机会(应该优先考虑额外HTTP往返的成本):

Get All People living in Moscow   with Iphone
Get All People living in Athens   with Iphone
Get All People living in Istanbul with Iphone
Get All People living in San Francisco with Iphone

现在您拥有了一部分ID,您可以以更低的成本查询最大的表格。以下是要执行的剩余查询:

EITHER
          have at least one metric of type X
          OR
          have at least one metric of type Y

因为表很大,所以我们应该尽量避免进行SCAN操作,并尝试通过主键查询所有内容。还应避免创建和维护索引,以最大限度地减少产生的存储空间和额外的写入成本。

我们已经有了person_id,现在我们需要过滤掉具有所需指标的那些,我们可以通过计算哈希和范围键来实现这一点。

我们再次需要更改表格结构:

people_metrics (id, person_id [HashKey], metric_type_index [RangeKey], metric_type, value)

范围键属性metric_type_index可以采用以下格式:

metric_type#calculated_number

*无论您使用什么作为范围键,请确保它使组合的哈希+范围键唯一且可以计算(详情如下)。

您的上一个查询可能是BatchGetItem,如下所示:

GetItem 1:

Table: people_metrics
Hash Key: 123 (person_id from the initial query)
Range Key: x#1

GetItem 2:

Table: people_metrics
Hash Key: 123 (person_id from the initial query)
Range Key: y#1

BatchGetItem应该非常快,只返回包含至少一个必需指标的记录。

如果您从第一个查询返回了大量的person_id记录,我建议您将第二个查询分成几个批次而不是一个巨大的BatchGetItem请求(BatchGetItem有一个{{3 }} 无论如何)。

我的建议可能不是最终答案,但我相信你可以得到一些想法,并发展到最终的最佳解决方案。

您可以在下面找到有关使用指南的详细信息:

  

设计表中项目的统一数据访问

     

"因为您正在随机化哈希键,所以写入表   每天均匀分布在所有哈希键值上;这个   将产生更好的并行性和更高的总吞吐量。 [...] 至   读取给定日期的所有项目,您仍然需要查询   每个2014-07-09.N键(其中N是1到200),和你的   应用程序需要合并所有结果。但是,你会的   避免单身"热"哈希密钥占用所有工作量。"

来源:100 items limit

这里有另一个有趣的观点,表明在单个分区中适度使用读取......

  

查询和扫描指南 - 避免突然爆发的阅读活动

     

"请注意,扫描使用的不仅仅是突发的容量单位   这是一个问题。这也是因为扫描很可能会消耗掉   因为扫描所有来自同一分区的容量单位   请求读取分区上彼此相邻的项目。这个   意味着请求正在命中同一个分区,导致所有   它的容量单位被消耗,并限制其他请求   那个分区。如果读取数据的请求已经传播开来   多个分区,那么操作就不会受到限制了   特定分区。"

最后,因为您正在使用时间序列数据,所以查看文档中建议的一些最佳实践可能会有所帮助:

  

了解时间序列数据的访问模式

     

对于您创建的每个表,指定吞吐量   要求。 DynamoDB分配和预留资源来处理您的   持续低延迟的吞吐量要求。当你设计   你的应用程序和表格,你应该考虑你的应用程序   访问模式,以最有效地使用您的表格   资源。

     

假设您设计了一个表来跟踪您网站上的客户行为,   例如他们点击的网址。您可以使用哈希和设计表   范围类型主键,客户ID作为哈希属性和   日期/时间作为范围属性。在此应用程序中,客户数据   随着时间的推移无限增长;但是,应用程序可能会显示   表格中所有项目的不均匀访问模式   最新的客户数据与您的应用程序可能更相关   随着时间的推移,更频繁地访问最新的项目   访问较少,最终很少访问旧项目。如果   这是一种已知的访问模式,您可以将其考虑在内   在设计表模式时。而不是存储所有项目   单个表,您可以使用多个表来存储这些项目。对于   例如,您可以创建表来存储月度或每周数据。对于   该表存储来自最近一个月或一周的数据,其中包含数据   访问速率很高,请求更高的吞吐量和表存储   较旧的数据,您可以调低吞吐量并节省资源。

     

您可以通过存储" hot"来节省资源。一个表中的项目   更高的吞吐量设置,"冷"另一个表中的项目   较低的吞吐量设置您只需删除即可删除旧项目   表格。您可以选择将这些表备份到其他存储   Amazon Simple Storage Service(Amazon S3)等选项。正在删除   整个表比删除项目更有效   一个接一个,它基本上使您的写吞吐量翻倍   与put操作一样多的删除操作。

来源:http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GuidelinesForTables.html