按日期查询DynamoDB

问题描述 投票:73回答:7

我来自关系数据库背景,并尝试使用亚马逊的DynamoDB

我有一个表,其中包含一个哈希键“DataID”和一个范围“CreatedAt”以及一系列项目。

我正在尝试获取在特定日期之后创建的所有项目并按日期排序。这在关系数据库中非常简单。

在DynamoDB中,我能找到的最接近的是查询并使用大于filter的范围键。唯一的问题是,为了执行查询,我需要一个破坏目的的哈希键。

那么我做错了什么?我的表架构是错误的,哈希键不应该是唯一的吗?还是有另一种查询方式?

amazon-web-services nosql amazon-dynamodb
7个回答
30
投票

更新答案:

DynamoDB允许指定二级索引以帮助进行此类查询。辅助索引可以是全局的,也就是说索引跨越散列键跨越整个表,或者本地意味着索引将存在于每个散列键分区中,因此在进行查询时也需要指定散列键。

对于此问题中的用例,您可能希望在“CreatedAt”字段上使用全局二级索引。

有关DynamoDB二级索引see the secondary index documentation的更多信息

原答案:

DynamoDB不允许仅对范围键进行索引查找。需要散列密钥,以便服务知道要查找哪个分区以查找数据。

您当然可以执行扫描操作以按日期值进行过滤,但这需要全表扫描,因此它并不理想。

如果需要跨多个主键按时间执行索引的索引查找,DynamoDB可能不是您使用的理想服务,或者您可能需要使用单独的表(在DynamoDB或关系存储中)来存储项目您可以执行索引查找的元数据。


27
投票

鉴于您当前的表结构,目前在DynamoDB中无法实现。最大的挑战是要理解表(分区)的哈希键应该被视为创建单独的表。在某些方面,这非常强大(将分区键视为为每个用户或客户创建新表等等)。

查询只能在单个分区中完成。这真的是故事的结局。这意味着如果您想按日期查询(您希望从epoch开始使用msec),那么您要在单个查询中检索的所有项目必须具有相同的哈希(分区键)。

我应该有资格这个。您绝对可以按照您要查找的标准来查找scan,这没有问题,但这意味着您将查看表中的每一行,然后检查该行是否具有与您的参数匹配的日期。这是非常昂贵的,特别是如果你是在第一时间按日期存储事件(即你有很多行)。

您可能想要将所有数据放在一个分区中来解决问题,但绝对可以,但是由于每个分区只接收总设置量的一小部分,因此吞吐量会非常低。

最好的办法是确定要创建的更有用的分区来保存数据:

  • 你真的需要查看所有行,还是只有特定用户的行?
  • 是否可以按月缩小列表范围,并进行多次查询(每月一次)?还是按年?
  • 如果你正在进行时间序列分析,有几个选项,将分区键更改为在PUT上计算的内容以使query更容易,或者使用另一个像kinesis这样的aws产品,它适合仅附加日志记录。

7
投票

您的哈希键(排序主键)必须是唯一的(除非您有其他人声明的范围)。

在您的情况下,要查询您的表,您应该有一个二级索引。

|  ID  | DataID | Created | Data |
|------+--------+---------+------|
| hash | xxxxx  | 1234567 | blah |

您的哈希密钥是ID您的二级索引定义为:DataID-Created-index(这是DynamoDB将使用的名称)

然后,您可以进行如下查询:

var params = {
    TableName: "Table",
    IndexName: "DataID-Created-index",
    KeyConditionExpression: "DataID = :v_ID AND Created > :v_created",
    ExpressionAttributeValues: {":v_ID": {S: "some_id"},
                                ":v_created": {N: "timestamp"}
    },
    ProjectionExpression: "ID, DataID, Created, Data"
};

ddb.query(params, function(err, data) {
    if (err) 
        console.log(err);
    else {
        data.Items.sort(function(a, b) {
            return parseFloat(a.Created.N) - parseFloat(b.Created.N);
        });
        // More code here
    }
});

基本上你的查询看起来像:

SELECT * FROM TABLE WHERE DataID = "some_id" AND Created > timestamp;

二级索引将增加所需的读/写容量单位,因此您需要考虑这一点。它仍然比扫描好很多,这在读取和时间上都是昂贵的(并且我认为限于100个项目)。

这可能不是最好的方法,但对于习惯于RD的人(我也习惯于SQL)来说,这是提高工作效率的最快方法。由于没有关于模式的限制,你可以掀起一些有效的东西,一旦你有了最有效的工作带宽,你就可以改变它。


5
投票

解决这个问题的方法是通过创建全局二级索引,如下所示。不确定这是否是最好的方法,但希望如果它对某人有用。

Hash Key                 | Range Key
------------------------------------
Date value of CreatedAt  | CreatedAt

对HTTP API用户施加的限制,以指定检索数据的天数,默认为24小时。

这样,我总是可以将哈希键指定为当前日期日,而RangeKey可以在检索时使用>和<运算符。这样,数据也分布在多个分片上。


3
投票

您可以将Hash键设置为“产品类别”id的行,然后将范围键作为时间戳与末尾附加的唯一ID的组合。这样你就知道了哈希键,并且仍然可以用大于的方式查询日期。


1
投票

您可以拥有多个相同的哈希键;但只有你有一个变化的范围键。把它想象成文件格式;只要格式不同,您就可以在同一文件夹中拥有2个同名文件。如果它们的格式相同,则它们的名称必须不同。同样的概念适用于DynamoDB的散列/范围键;只需将哈希视为名称,将范围视为格式。

此外,我不记得他们在OP时是否有这些(我不相信他们这样做),但他们现在提供本地二级索引。

我对这些的理解是它现在应该允许您执行所需的查询而无需进行完整扫描。缺点是必须在创建表时指定这些索引,并且(我相信)在创建项目时也不能为空。此外,它们需要额外的吞吐量(虽然通常没有扫描那么多)和存储,因此它不是一个完美的解决方案,但对于某些人来说是一个可行的替代方案。

我仍然建议将Mike Brant的答案作为使用DynamoDB的首选方法;并自己使用该方法。在我的例子中,我只有一个中心表,只有一个哈希键作为我的ID,然后是具有可以查询的哈希和范围的辅助表,然后该项将代码指向中心表的“感兴趣的项目”,直接。

有关二级索引的其他数据可以在亚马逊的DynamoDB文档here中找到。

无论如何,希望这将有助于在这个线程上发生的任何其他人。


-9
投票

更新的答案使用具有可预测吞吐量的Dynamo DB查询没有方便的方法。一个(次优选)选项是使用具有人工HashKey和CreatedAt的GSI。然后单独通过HashKey查询并提及ScanIndexForward来订购结果。如果你能想出一个自然的HashKey(比如项目的类别等)那么这个方法就是赢家。另一方面,如果为所有项目保留相同的HashKey,那么当数据集超过10GB(一个分区)时,它将主要影响吞吐量

原始答案:您现在可以使用GSI在DynamoDB中执行此操作。将“CreatedAt”字段设置为GSI并发出类似(GT some_date)的查询。对于此类查询,将日期存储为数字(自纪元以来的msecs)。

详细信息如下:全球二级索引 - Amazon DynamoDB:http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.Using

这是一个非常强大的功能。请注意,查询仅限于(EQ | LE | LT | GE | GT | BEGINS_WITH | BETWEEN)条件 - Amazon DynamoDB:http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html

© www.soinside.com 2019 - 2024. All rights reserved.