135

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

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

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

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

据我所知,我的选择是:

A:执行 Scan 操作,对每个返回的项目调用 delete,直到没有剩余项目

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

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

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

4

9 回答 9

66

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

确实是一个可以理解的要求;我可以想象 AWS 团队可能会随着时间的推移添加此类高级操作(他们有先从有限的功能集开始并根据客户反馈评估扩展的历史),但您应该采取以下措施来避免成本至少完整扫描:

  1. 使用Query而不是Scan来检索所有项目user_id- 无论使用中的组合散列/范围主键如何,这都有效,因为HashKeyValueRangeKeyCondition是此 API 中的单独参数,前者仅针对复合散列组件的属性值首要的关键。.

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

      从中继续先前查询的项目的主键。如果查询操作在完成查询之前中断,则较早的查询可能会将此值作为 LastEvaluatedKey 提供;由于结果集大小或 Limit 参数。LastEvaluatedKey 可以在新的查询请求中传回,以从该点继续操作。

  2. 循环遍历所有返回的项目,或者像往常一样促进DeleteItem

    • 更新:很可能BatchWriteItem更适合这样的用例(详见下文)。

更新

正如ivant所强调的那样,BatchWriteItem操作使您能够在单个 API 调用中跨多个表放置或删除多个项目 [强调我的] :

要上传一项,您可以使用 PutItem API;要删除一项,您可以使用 DeleteItem API。但是,当您要上传或删除大量数据时,例如从 Amazon Elastic MapReduce (EMR) 上传大量数据或将数据从另一个数据库迁移到 Amazon DynamoDB,此 API 提供了一种有效的替代方案。

请注意,这仍然有一些相关的限制,最值得注意的是:

  • 单个请求中的最大操作数——您最多可以指定总共 25 个 put 或 delete 操作;但是,总请求大小不能超过 1 MB(HTTP 负载)。

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

尽管如此,这显然为手头的用例提供了潜在的显着收益。

于 2012-02-06T11:30:30.750 回答
48

根据 DynamoDB 文档,您可以删除整个表。

见下文:

“删除整个表比逐个删除项目效率高得多,这基本上使写入吞吐量翻倍,因为您执行的删除操作与放置操作一样多”

如果您只想删除数据的一个子集,那么您可以为每个月、每个年或类似的时间制作单独的表格。这样您就可以删除“上个月”并保持其余数据完好无损。

这是您使用 AWS 开发工具包在 Java 中删除表的方式:

DeleteTableRequest deleteTableRequest = new DeleteTableRequest()
  .withTableName(tableName);
DeleteTableResult result = client.deleteTable(deleteTableRequest);
于 2013-04-15T09:51:06.510 回答
19

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

在您的情况下,我会在日志过期时添加 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

于 2018-06-13T20:59:57.510 回答
6

这个问题的答案取决于项目的数量及其大小和您的预算。取决于我们有以下3种情况:

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

2-我们的表中有很多项目或非常大的项目,我们可以根据时间将它们存储到不同的表中。然后正如乔纳森所说,您可以删除该表。这要好得多,但我认为它与您的情况不匹配。因为无论创建日志的时间是什么时候,您都想删除所有用户数据,所以在这种情况下,您不能删除特定表。如果您想为每个用户设置一个单独的表格,那么我想如果用户数量很高,那么它会很贵,而且对您的情况不切实际。

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

于 2016-05-26T07:16:47.093 回答
2

我们没有截断发电机表的选项。我们必须删除表并重新创建。DynamoDB 费用基于 ReadCapacityUnits 和 WriteCapacityUnits 。如果我们使用 BatchWriteItem 函数删除所有项目,它将使用 WriteCapacityUnits。所以最好删除特定记录或删除表并重新开始。

于 2018-06-06T15:07:19.173 回答
2

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

删除特定记录

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

缺点:一次只能删除一项

于 2021-07-26T15:34:29.077 回答
1

考虑过使用测试通过变量吗?就像是:

测试输入将类似于:

{
  "TABLE_NAME": "MyDevTable",
  "PARTITION_KEY": "REGION",
  "SORT_KEY": "COUNTRY"
}

调整您的代码以接受输入:

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

exports.handler = async (event) => {
    const TABLE_NAME = event.TABLE_NAME;
    const PARTITION_KEY = event.PARTITION_KEY;
    const SORT_KEY = event.SORT_KEY;
    let params = {
        TableName: TABLE_NAME,
    };
    console.log(`keys: ${PARTITION_KEY} ${SORT_KEY}`);

    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) {
        // console.log(`item: ${i[PARTITION_KEY] } ${i[SORT_KEY]}`);
        const deleteReq = {DeleteRequest: {Key: {},},};
        deleteReq.DeleteRequest.Key[PARTITION_KEY] = i[PARTITION_KEY];
        deleteReq.DeleteRequest.Key[SORT_KEY] = i[SORT_KEY];

        // console.log(`DeleteRequest: ${JSON.stringify(deleteReq)}`);
        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;
};
于 2020-11-27T08:25:58.487 回答
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/CloudFormation 创建表,删除表然后从 Web 控制台重新创建可能会导致问题。

于 2019-01-23T14:32:45.500 回答
0

这是我用来删除所有项目的递归函数batchWriteItems。定义表的键模式和表名并调用clearTable

var AWS = require("aws-sdk");
var docClient = new AWS.DynamoDB.DocumentClient();

const TABLE_NAME = ""
const TABLE_PRIMARY_KEY = ""

const clearTable = async () => {

    const batch = await getItemBatch();

    await recursiveDeleteTableItems(batch)

}

const recursiveDeleteTableItems = async (batch) => {

    if(batch && batch.length > 0) {
      await deleteItemBatch(batch)
    } else {
      return
    }

    const newItemBatch = await getItemBatch()

    await recursiveDeleteTableItems(newItemBatch)

}

const deleteItemBatch = async (batch) => {

   const deleteOperations = batch.map( i => ({ 
     "DeleteRequest": { 
       "Key": { 
        [TABLE_PRIMARY_KEY] : i.KEY_VALUE
       }
     }
   }))

   return new Promise(async (resolve, reject) => {

     const params = {
       "RequestItems": {
         [TABLE_NAME]: deleteOperations
       }
     }

     docClient.batchWrite(params, (err, data) => {

       if (err) {
         reject(`Unable to query. Error: ${err} ${JSON.stringify(err, null, 2)}`);
         return
       }

       resolve(data)

      })

    })

}

const getItemBatch = async () => {

  var params = {
    TableName: TABLE_NAME,
    Limit: 25 // match batchWriteItem
  };

  return new Promise(async (resolve, reject) => {

    docClient.scan(params, async function (err, data) {

        if (err) {
            reject(`Unable to query. Error: ${err} ${JSON.stringify(err, null, 2)}`);
            return
        }

        resolve(data.Items)

      });
  });

}
于 2021-12-17T11:38:58.463 回答