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

137 人关注

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

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

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

做这种操作的推荐方法是什么(考虑到可能有数百万个项目要删除)?

据我所知,我的选择是。

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

B: 执行一个BatchGet操作,同样对每个项目调用删除,直到没有项目为止。

这两种方法对我来说都很糟糕,因为它们会花费很长的时间。

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

database
nosql
amazon-web-services
cloud
amazon-dynamodb
Tyler
Tyler
发布于 2012-02-06
9 个回答
Steffen Opel
Steffen Opel
发布于 2020-10-19
已采纳
0 人赞同

我最想做的是调用LogTable.DeleteItem(user_id) - 而不提供范围。 而不提供范围,并让它为我删除所有东西。

这确实是一个可以理解的要求;我可以想象像这样的高级操作可能会随着时间的推移被AWS团队添加(他们有一个历史,就是先从有限的功能集开始,然后根据客户的反馈评估扩展),但这里是你应该做的,至少可以避免全面扫描的成本。

  • 使用 查询 而不是 扫描 来检索所有的项目, user_id - 这在使用哈希/范围主键的情况下都有效,因为 HashKeyValue RangeKeyCondition 在这个API中是独立的参数,前者只针对 复合主键的哈希组件的属性值。

  • 请注意,你必须像往常一样在这里处理查询API分页,见 ExclusiveStartKey 参数。

    项目的主键,从这里继续先前的查询。一个较早的查询可能会提供这个值作为 先前的查询可能会提供这个值作为LastEvaluatedKey,如果该查询操作在完成之前被中断的话。 如果该查询操作在完成查询前被中断,或者由于结果集的大小或 因为结果集的大小或限制参数的原因。最后评估键 LastEvaluatedKey可以在一个新的查询请求中传回,以继续 从这一点上继续操作。

  • 循环所有返回的项目,并像往常一样促进 DeleteItem

  • 更新 :很可能 BatchWriteItem 更适合于这样的用例(详见下文)。
  • 要上传一个项目,你可以使用PutItem API,而要删除一个 项目,你可以使用DeleteItem API。然而,当你想上传 或删除大量的数据时,例如从Amazon Elastic MapReduce上传大量的 数据或将数据从其他数据库迁移到Amazon DynamoDB时,就需要使用PutItem API。 数据库迁移到Amazon DynamoDB,这个API提供了一个有效的 替代方案。

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

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

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

  • 尽管如此,这显然为像眼前这样的用例提供了潜在的巨大收益。

    我认为第二步使用批量删除是有意义的(它被 "掩盖 "为 批量写操作 )。
    @ivant - 非常感谢你的提示,BatchWriteItem的这种 "屏蔽式 "删除功能当时确实让我忽略了;我已经相应地更新了答案。
    Neil
    删掉 BatchWriteItem 的项目需要通过以下方式指定 TableWriteItems
    我意识到这是旧的,而且OP没有提到具体的语言SDK,但在Python中,有一个高水平的 batch_writer() ,作为 boto3.resource.Table API的一部分,它将 "自动处理缓冲和分批发送项目。此外,批处理程序还将自动处理任何未处理的项目,并根据需要重新发送。"也就是说,它是一个围绕BatchWriteItem的包装器,管理恼人的部分。 boto3.amazonaws.com/v1/documentation/api/latest/reference/ ...
    jonathan
    jonathan
    发布于 2020-10-19
    0 人赞同

    根据DynamoDB文档,你可以直接删除整个表。

    "删除整个表要比逐个删除项目的效率高得多,因为你做的删除操作和放的操作一样多,所以写的吞吐量基本上是翻倍的"

    如果你希望只删除数据的一个子集,那么你可以为每个月、每年或类似的情况制作单独的表。这样你就可以删除 "上个月",并保持其余的数据不变。

    这就是你如何使用AWS SDK在Java中删除一个表。

    DeleteTableRequest deleteTableRequest = new DeleteTableRequest()
      .withTableName(tableName);
    DeleteTableResult result = client.deleteTable(deleteTableRequest);
        
    我也喜欢这个答案,但要注意:这可能会在你的系统中创建许多表,而我们是按表付费的。因此,你需要在月底后减少供应量(如果你的表是按月计算的),而这个表并没有被删除。
    同意这个答案,如果你需要删除表中的所有记录,它是适用的,但这里的提问者想删除的是用户基础条目而不是整个表。
    考虑到DynamoDB的定价,为每个用户设置一个单独的表会很昂贵。每月一张表实际上会使事情变得更糟。这显然是对一个不同的、非常具体的问题的回答。
    考虑到DynamoDB的定价,为每个用户设置一个单独的表会很昂贵。每月一张表实际上会使事情变得更糟。这显然是对一个不同的、非常具体的问题的回答。
    如果你使用自动化配置,如CloudFormation来管理你的表作为堆栈的一部分,删除表可能也不是一个有吸引力的选择。我不知道有什么简单的方法可以让CloudFormation重新创建一个被你手工删除的表。
    Lukas Liesis
    Lukas Liesis
    发布于 2020-10-19
    0 人赞同

    如果你想在一段时间后删除项目,例如一个月后,只需使用Time To Live选项。它将 计算写入单位。

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

    当表上的Time To Live被启用时,一个后台工作会检查 TTL属性,看它们是否过期。

    DynamoDB通常在过期后的48小时内删除过期的项目。 过期。一个项目在过期后真正被删除的确切时间 过期后,项目真正被删除的确切时间是根据工作负载的性质和 表的大小。已经过期但未被删除的项目将 仍然显示在读取、查询和扫描中。这些项目仍然可以被 更新,成功的更新改变或删除过期属性的 属性的成功更新将被尊重。

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

    增加TTL是一个 "更新"(写操作)。我不确定做 "更新 "而不是 "删除 "会有什么好处。
    你可以用原来的写法插入这些数据,用任何其他的更新动作来更新。当然,如果你有一堆数据,然后你想删除它,这就不是一个选项。但对于你可以为你插入或更新的数据设置ttl的情况,这是一个有效的选择。
    我同意,如果已经配置了TTL,而且清理工作可以等待48小时,这绝对是最佳选择。如果我说得不清楚,我道歉。
    Iman Sedighi
    Iman Sedighi
    发布于 2020-10-19
    0 人赞同

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

    1- 表中项目的数量和大小不是很多。那么正如Steffen Opel所说,你可以使用查询而不是扫描来检索user_id的所有项目,然后在所有返回的项目上进行循环,并促进 DeleteItem BatchWriteItem 。但请记住,你可能会在这里消耗大量的吞吐能力。例如,考虑一种情况,你需要从DynamoDB表中删除1000个项目。假设每个项目的大小是1KB,导致大约1MB的数据。这个批量删除任务将需要查询和删除总共2000个写容量单位。为了在10秒内完成这个数据负载(这在某些应用中甚至不被认为是快的),你需要将表的配置写入吞吐量设置为200个写入容量单位。正如你所看到的,如果项目数量少或项目规模小,使用这种方式是可行的。

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

    3- 如果你有大量的数据,你不能把你的热数据和冷数据分成不同的表,而且你需要经常做大规模的删除,那么不幸的是,DynamoDB对你来说不是一个好的选择。它可能变得更加昂贵或非常缓慢(取决于你的预算)。在这种情况下,我建议为你的数据寻找另一个数据库。

    Shravan Hebbar
    Shravan Hebbar
    发布于 2020-10-19
    0 人赞同

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

    Isaac I9
    Isaac I9
    发布于 2020-10-19
    0 人赞同

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

    删除特定记录

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

    缺点:一次只能删除一个项目

    NubPro
    NubPro
    发布于 2020-10-19
    0 人赞同

    想过用测试来传入变量吗?比如说。

    测试输入会是这样的。

    "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;
        
    Attaque
    Attaque
    发布于 2020-10-19
    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)
        
    Mohammad
    Mohammad
    发布于 2020-10-19
    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>();