我来自关系数据库背景并尝试使用亚马逊的DynamoDB
我有一个表格,其中包含一个散列键“DataID”和一个范围“CreatedAt”以及其中的一堆项目。
我正在尝试获取在特定日期之后创建并按日期排序的所有项目。这在关系数据库中非常简单。
在DynamoDB中,我能找到的最接近的是查询并使用大于filter的范围键。唯一的问题是,为了执行查询,我需要一个能够达到目的的哈希键。
那么我做错了什么?我的表架构是错误的,哈希键不应该是唯一的吗?还是有其他方式来查询?
答案 0 :(得分:31)
更新的答案:
DynamoDB允许指定二级索引以帮助进行此类查询。辅助索引可以是全局的,也就是说索引跨越散列键跨越整个表,或者本地意味着索引将存在于每个散列键分区中,因此在进行查询时也需要指定散列键。
对于此问题中的用例,您可能希望在“CreatedAt”字段上使用全局二级索引。
有关DynamoDB二级索引see the secondary index documentation
的更多信息原始答案:
DynamoDB不允许仅对范围键进行索引查找。需要散列密钥,以便服务知道要查找哪个分区以查找数据。
您当然可以执行扫描操作以按日期值进行过滤,但这需要全表扫描,因此不太理想。
如果需要跨多个主键按时间执行索引的索引查找,DynamoDB可能不是您使用的理想服务,或者您可能需要使用单独的表(在DynamoDB或关系存储中)存储可以对其执行索引查找的项元数据。
答案 1 :(得分:30)
鉴于您当前的表结构,目前在DynamoDB中无法实现。最大的挑战是要理解表(分区)的哈希键应该被视为创建单独的表。在某些方面,这非常强大(想想分区键为每个用户或客户创建一个新表等等。)
查询只能在一个分区中完成。这真的是故事的结局。这意味着如果您想按日期查询(您希望从epoch开始使用msec),那么您要在单个查询中检索的所有项目必须具有相同的哈希(分区键)。
我应该对此有资格。根据您要查找的标准,您绝对可以scan
,这没有问题,但这意味着您将查看表中的每一行,然后检查该行是否具有匹配的日期你的参数。这是非常昂贵的,特别是如果你是在第一时间按日期存储事件(即你有很多行)。
您可能想要将所有数据放在一个分区中来解决问题,但绝对可以,但是由于每个分区只接收总设置量的一小部分,因此吞吐量会非常低。
最好的办法是确定要创建的更有用的分区来保存数据:
您真的需要查看所有行,还是只有特定用户的行?
首先按月缩小列表是否可以,并进行多次查询(每月一次)?还是按年?
如果您正在进行时间序列分析,有几个选项,将分区键更改为PUT
上计算的内容以使query
更容易,或使用其他aws产品(如kinesis)这适用于追加记录。
答案 2 :(得分: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),这是提高工作效率的最快方法。由于没有关于模式的限制,你可以掀起一些有效的东西,一旦你有了最有效的工作带宽,就可以改变现状。
答案 3 :(得分:5)
我遵循的方法是通过创建全局二级索引来解决这个问题,如下所示。不确定这是否是最好的方法,但希望它对某人有用。
Hash Key | Range Key
------------------------------------
Date value of CreatedAt | CreatedAt
对HTTP API用户施加限制以指定检索数据的天数,默认为24小时。
这样,我总是可以将HashKey指定为当前日期,而RangeKey可以使用>和<检索时的运算符。这样,数据也分布在多个分片上。
答案 4 :(得分:3)
您可以将Hash键设置为'产品类别' id,然后将范围键作为时间戳与末尾附加的唯一ID的组合。这样你就知道了哈希键,并且仍然可以用大于的方式查询日期。
答案 5 :(得分:1)
您可以拥有多个相同的哈希键;但只有你有一个变化的范围键。把它想象成文件格式;只要格式不同,您就可以在同一文件夹中拥有2个同名文件。如果它们的格式相同,则它们的名称必须不同。同样的概念适用于DynamoDB的散列/范围键;只需将哈希视为名称,将范围视为格式。
此外,我不记得他们在OP时是否有这些(我不相信他们这样做),但他们现在提供本地二级索引。
我对这些的理解是它现在应该允许您执行所需的查询而无需进行完整扫描。缺点是必须在创建表时指定这些索引,并且(我相信)在创建项目时也不能为空。此外,它们需要额外的吞吐量(虽然通常没有扫描那么多)和存储,因此它不是一个完美的解决方案,但对于某些人来说是一个可行的替代方案。
我仍然建议将Mike Brant的答案作为使用DynamoDB的首选方法;并自己使用该方法。在我的例子中,我只有一个中心表,只有一个哈希键作为我的ID,然后是具有可以查询的哈希和范围的辅助表,然后该项将代码指向中心表的“感兴趣的项目”,直接
有关二级索引的其他数据可以在亚马逊的DynamoDB文档here中找到感兴趣的人。
无论如何,希望这会帮助其他任何发生在这个线程上的人。
答案 6 :(得分:-1)
工作查询 1.aws dynamodb scan --table-name tableName --region us-east-1 --filter-expression "begins_with(createdTm,:gen)" --expression-attribute-values "{ :gen":{ "S":"2021-04-15"} 选择“COUNT”
2.aws dynamodb scan --table-name tableName --region us-east-1 --filter-expression "createdTm BETWEEN :v1 AND :v2" --expression-attribute-values '{":v1": {"S":"2021-04-13"}, ":v2":{"S":"2021-04-14"}}' --select "COUNT"
答案 7 :(得分:-9)
更新了答案 使用具有可预测吞吐量的Dynamo DB查询没有方便的方法。一个(次优选)选项是使用具有人工HashKey&的GSI。 CreatedAt。然后单独通过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