从DynamoDB中删除大量项目的推荐方法是什么?

时间:2012-02-06 00:00:22

标签: database nosql amazon-web-services cloud amazon-dynamodb

我正在DynamoDB中编写一个简单的日志记录服务。

我有一个日志表,该表由user_id哈希和时间戳(Unix epoch int)范围键入。

当服务的用户终止其帐户时,无论范围值如何,我都需要删除表格中的所有项目。

进行此类操作的推荐方法是什么(请记住可能有数百万项要删除)?

据我所知,我的选择是:

答:执行扫描操作,在每个返回的项目上调用删除,直到没有项目为止

B:执行BatchGet操作,再次对每个项目调用delete,直到没有剩下

这些对我来说都很糟糕,因为它们需要很长时间。

我理想的做法是调用LogTable.DeleteItem(user_id) - 不提供范围,并让它为我删除所有内容。

8 个答案:

答案 0 :(得分:43)

  

我理想的做法是调用LogTable.DeleteItem(user_id) -   没有提供范围,并让它为我删除所有内容。

确实可以理解的要求;我可以想象这些高级操作可能会随着时间的推移而被AWS团队添加(他们有首先从有限的功能集开始并根据客户反馈评估扩展的历史),但这是你应该做的,以避免成本至少完整扫描:

  1. 使用Query而不是Scan来检索user_id的所有项目 - 无论使用何种组合哈希/范围主键,这都有效,因为 HashKeyValue < / em>和 RangeKeyCondition 是此API中的单独参数,前者仅针对复合主键的哈希组件的属性值。

    • 请注意,您必须像往常一样处理查询API分页,请参阅 ExclusiveStartKey 参数:
        

      要从中继续先前查询的项的主键。一个   如果更早,查询可能会将此值提供为LastEvaluatedKey   查询操作在完成查询之前被中断;或   因为结果集大小或Limit参数。该   LastEvaluatedKey可以在新的查询请求中传回以继续   从那时起的操作。

  2. 循环播放所有返回的项目,并照常提供DeleteItem

    • 更新:最有可能BatchWriteItem更适合此类用例(详见下文)。

  3. 更新

    正如ivant所强调的那样,BatchWriteItem操作可让您在单个API调用中跨多个表放置或删除多个项目[强调我的] < / EM>:

      

    要上传一个项目,您可以使用PutItem API并删除一个   item,您可以使用DeleteItem API。但是,当您要上传时   或删除大量数据,例如上传大量数据   来自Amazon Elastic MapReduce(EMR)的数据或从另一个迁移数据   数据库到Amazon DynamoDB,这个API提供了一个高效的   替代品。

    请注意,这仍有一些相关的限制,最明显的是:

    • 单个请求中的最大操作数 - 您最多可以指定25个放置或删除操作;但是,总请求大小不能超过1 MB(HTTP有效负载)。

    • 不是原子操作 - BatchWriteItem中指定的各个操作是原子操作;但BatchWriteItem作为一个整体是“尽力而为”的操作而不是原子操作。也就是说,在BatchWriteItem请求中,某些操作可能会成功,而其他操作可能会失败。 [...]

    然而,这显然为手头的用例提供了潜在的显着收益。

答案 1 :(得分:42)

根据DynamoDB文档,您可以删除完整的表格。

见下文:

“删除整个表比逐个删除项目效率要高得多,这实际上会使写入吞吐量翻倍,就像执行多次删除操作一样”

如果您只想删除数据的一部分,那么您可以为每个月,每年或类似的表单创建单独的表。通过这种方式,您可以删除“上个月”并保持其余数据的完整性。

这是使用AWS SDK在Java中删除表的方法:

DeleteTableRequest deleteTableRequest = new DeleteTableRequest()
  .withTableName(tableName);
DeleteTableResult result = client.deleteTable(deleteTableRequest);

答案 2 :(得分:8)

如果您想在一段时间后删除项目,例如一个月后,只需使用生存时间选项。 计算写入单位。

在您的情况下,我会在日志过期时添加ttl,并在删除用户后保留这些内容。 TTL会确保最终删除日志。

  

如果在表上启用了生存时间,后台作业将检查   项目的TTL属性是否过期。

     

DynamoDB通常会在48小时内删除过期的项目   过期。项目真正被删除的确切持续时间   到期后特定于工作量的性质和   桌子的大小。已过期但尚未删除的项目将会   仍显示在读取,查询和扫描中。这些物品仍然可以   更新和成功更新以更改或删除过期   属性将被尊重。

https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/howitworks-ttl.html

答案 3 :(得分:2)

此问题的答案取决于商品数量及其尺寸和预算。取决于我们有以下3个案例:

1-表中的项目数和项目大小不是很多。然后Steffen Opel表示你可以使用Query而不是Scan来检索user_id的所有项目,然后遍历所有返回的项目,并促进DeleteItemBatchWriteItem。但请记住,这里可能会消耗大量的吞吐量。例如,考虑一种情况,您需要从DynamoDB表中删除1000个项目。假设每个项目的大小为1 KB,从而产生大约1 MB的数据。此批量删除任务将需要总共2000个写入容量单位进行查询和删除。要在10秒内执行此数据加载(在某些应用程序中甚至不被视为快速加载),您需要将表的预配置写入吞吐量设置为200个写入容量单位。正如你可以看到它可以使用这种方式,如果它用于较少数量的项目或小尺寸项目。

2-我们在表格中有很多项目或非常大的项目,我们可以根据时间将它们存储到不同的表格中。然后作为 jonathan说你可以删除表。这要好得多,但我不认为它与你的情况相符。由于您希望删除所有用户数据,无论创建日志的时间是什么,因此在这种情况下您无法删除特定表。如果你想为每个用户提供一个单独的表格,那么我想如果用户数量很高,那么它的价格非常昂贵,对你的情况来说是不切实际的。

3-如果您有大量数据并且无法将热数据和冷数据划分到不同的表中,并且需要经常进行大规模删除,那么不幸的是DynamoDB根本不适合您。它可能会变得更贵或非常慢(取决于您的预算)。在这些情况下,我建议为您的数据找到另一个数据库。

答案 4 :(得分:1)

所以只是一个更新,DynamoDB 控制台上有一个版本,其中包括一个称为 PartiQL 编辑器的新功能。它是一个用于 DynamoDB 操作的类似 SQL 的编辑器。

删除特定记录

DELETE FROM <Table-Name> WHERE id=some-Id;

缺点:一次只能删除一项

答案 5 :(得分:0)

我们没有截断发电机表的选项。我们必须放弃桌子并重新创建。 DynamoDB费用基于ReadCapacityUnits&amp; WriteCapacityUnits。如果我们使用BatchWriteItem函数删除所有项目,它将使用WriteCapacityUnits.So更好地删除特定记录或删除表格并重新开始。

答案 6 :(得分:0)

我删除DynamoDb表中所有行的方法只是使用DynamoDbs ScanAsync从表中拉出所有行,然后将结果列表馈送到DynamoDbs AddDeleteItems。 下面的C#代码对我来说很好。

        public async Task DeleteAllReadModelEntitiesInTable()
    {
        List<ReadModelEntity> readModels;

        var conditions = new List<ScanCondition>();
        readModels = await _context.ScanAsync<ReadModelEntity>(conditions).GetRemainingAsync();

        var batchWork = _context.CreateBatchWrite<ReadModelEntity>();
        batchWork.AddDeleteItems(readModels);
        await batchWork.ExecuteAsync();
    }

注意:如果使用YAML / CloudFront创建表,则删除该表然后从Web控制台重新创建它可能会导致问题。

答案 7 :(得分:0)

我再放一次。

这是我在nodeJS中执行的lambda。它将在表上进行全面扫描,然后每个请求每25个项目批量删除。

请记住要更改TABLE_NAME

const AWS = require('aws-sdk');

const docClient = new AWS.DynamoDB.DocumentClient({ apiVersion: '2012-08-10' });

//const { TABLE_NAME } = process.env;
TABLE_NAME = "CHANGE ME PLEASE"

exports.handler = async (event) => {
    let params = {
        TableName: TABLE_NAME,
    };

    let items = [];
    let data = await docClient.scan(params).promise();
    items = [...items, ...data.Items];

    while (typeof data.LastEvaluatedKey != 'undefined') {
        params.ExclusiveStartKey = data.LastEvaluatedKey;

        data = await docClient.scan(params).promise();
        items = [...items, ...data.Items];
    }

    let leftItems = items.length;
    let group = [];
    let groupNumber = 0;

    console.log('Total items to be deleted', leftItems);

    for (const i of items) {
        const deleteReq = {
            DeleteRequest: {
                Key: {
                    id: i.id,
                },
            },
        };

        group.push(deleteReq);
        leftItems--;

        if (group.length === 25 || leftItems < 1) {
            groupNumber++;

            console.log(`Batch ${groupNumber} to be deleted.`);

            const params = {
                RequestItems: {
                    [TABLE_NAME]: group,
                },
            };

            await docClient.batchWrite(params).promise();

            console.log(
                `Batch ${groupNumber} processed. Left items: ${leftItems}`
            );

            // reset
            group = [];
        }
    }

    const response = {
        statusCode: 200,
        //  Uncomment below to enable CORS requests
        //  headers: {
        //      "Access-Control-Allow-Origin": "*"
        //  },
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};