【问题标题】:Get all items from a table without Scan无需扫描即可从表中获取所有项目
【发布时间】:2021-03-13 03:28:12
【问题描述】:

目前,我有一个使用 SCAN 选项从 DynamoDB 表中获取所有项目的功能。这是一种昂贵的方法,我更喜欢使用 QUERY 选项。但是查看文档似乎没有一种简单的方法可以使用 QUERY 选项检索所有项目 - 它需要某种条件。

示例

var params = {
    TableName : "Movies",
    KeyConditionExpression: "#yr = :yyyy",
    ExpressionAttributeNames:{
        "#yr": "year"
    },
    ExpressionAttributeValues: {
        ":yyyy": 1985
    }
};

docClient.query(params, function(err, data) {
    if (err) {
        console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
    } else {
        console.log("Query succeeded.");
        data.Items.forEach(function(item) {
            console.log(" -", item.year + ": " + item.title);
        });
    }
});

预期

var params = {
    TableName : "Movies"  
};

docClient.query(params, function(err, data) {
    if (err) {
        console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
    } else {
        console.log("Query succeeded.");
        data.Items.forEach(function(item) {
            console.log(" -", item.year + ": " + item.title);
        });
    }
});

是否可以使用 QUERY 从表中检索所有数据?我想过使用 BEGINS_WITH 等,但所有主键都是不同的/随机的,并且不以特定字符或短语开头。

【问题讨论】:

  • 假设您可以使用 query 获取所有项目,这会比使用 scan 在实质上更好吗?
  • @jarmod 扫描操作通常更慢且更昂贵,因为该操作必须遍历表中的每个项目以获取我请求的项目。这是我不想使用扫描的主要原因..
  • 但是无论如何你都会得到所有的项目,除非你实际上并不是指“所有项目”。事实上,查询路由可能会比扫描慢。
  • 所有项目都正确。我将通过一些研究点,例如下面的链接,其中指出查询操作预计会非常快,并且仅比 get 操作慢一点。另一方面,扫描操作可能需要 50-100 毫秒到几个小时才能完成,具体取决于表的大小。我对此的理解可能是错误的.. techtraits.com/cloud/nosql/2012/06/28/…
  • 它们是不同的。与预先知道分区键和排序键(或排序键范围)的查询相比,扫描查找特定项目需要更长的时间。但是您说的是“所有项目”,因此事先没有已知的密钥,因此,在理论上查询可以为您提供所有项目的世界中,它不会比扫描更好。也就是说,查询提供了获取所有项目的方法,所以这是一个有争议的问题。

标签: node.js amazon-web-services amazon-dynamodb


【解决方案1】:

从技术上讲,Amazon DynamoDB 表中所有项目的 query 返回的数据量与 scan 返回的数据量相同,因此成本应该没有差异。

scan 操作通常会降低效率是因为它必须读取整个表,然后过滤掉值以提供所需的结果,本质上增加了从结果集中删除数据的额外步骤.如果您想读取整个表而不进行过滤,scanquery 都必须检索所有值,并且没有额外的过滤步骤。

【讨论】:

  • 有道理。是的,我没有要应用的过滤器,只是从表中返回所有数据。只是经过一些研究,似乎由于成本较高而应避免 SCAN —— 尽管 SCAN 和 QUERY 都可以返回所有数据,但它们两个操作返回数据的方式却大不相同。但是,如果我无论如何要返回所有数据,这可能不适用..
【解决方案2】:

通过查询进行的唯一方法是分别循环遍历每个分区键。

我建议您查看围绕您的查询构建的二级索引,这将更有效:https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SecondaryIndexes.html

【讨论】:

  • 是的,我将 GSI 用于不同的表用于不同的目的。但我确实知道如何使用它 - 如果我错了,请纠正我,但如果我创建了 GSI,例如用户索引,它可以包含所有记录的“用户”值。然后我可以只查询索引匹配“用户”的表?
【解决方案3】:

如果你想获取所有数据,你可以使用scan all data,但是我建议你通过limit和pagination来获取数据,因为如果你在dynamodb上有数百万的数据,它会消耗大量的内存资源。 这种获取所有数据的方法

const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({
    apiVersion: '2012-08-10',
    region: 'ap-southeast-1' // put your region
});

exports.handler = async (event, context, callback) => {
    const tableName = event.params.querystring.tablename; 
     let params = { 
         TableName: tableName
     };

     let scanResults = [];
     let items;

     do {
         items = await docClient.scan(params).promise();
         items.Items.forEach((item) => scanResults.push(item));
         params.ExclusiveStartKey = items.LastEvaluatedKey;
     } while (typeof items.LastEvaluatedKey != "undefined");

     callback(null, scanResults);
   
            
};

但是通过下面的方法,在你获取数据后,你需要将LastEvaluatedKey从前端发布到params,你可以将它用作ExclusiveStartKey。

const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({
    apiVersion: '2012-08-10',
    region: 'ap-southeast-1' // put your region
});

exports.handler = async (event, context, callback) => {
    const tableName = event.params.querystring.tablename; 
    
    let pageSize = event.params.querystring.pagesize;
    let lastItem = event.params.querystring.lastItem;
        try {
              const params = {
                TableName: tableName,
                Limit: pageSize,
              };
              if (lastItem) {
                params.ExclusiveStartKey = { id: lastItem};
              }
              const response = await docClient.scan(params).promise();
              return {
                 items: response.Items,
                 lastItem: response.LastEvaluatedKey
              };
        
            } catch (error) {
              throw error;
            }
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-19
    • 2013-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-20
    • 1970-01-01
    相关资源
    最近更新 更多