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


111

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

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

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

建议进行这种操作的方法是什么(请记住可能要删除数百万个项目)?

据我所知,我的选择是:

答:执行扫描操作,对每个返回的项目调用delete,直到没有剩余项目为止

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

这两个对我来说都很糟糕,因为它们将花费很长时间。

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

Answers:


52

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

确实是可以理解的要求;我可以想象,随着时间的推移,AWS团队可能会添加类似的高级操作(他们有先从有限的功能集入手,并根据客户反馈评估扩展的历史),但是这是您应该采取的避免操作成本的方法全面扫描至少:

  1. 使用Query而不是Scan来检索所有项目user_id-无论使用的哈希/范围主键组合如何,此方法均有效,因为HashKeyValueRangeKeyCondition是此API中的单独参数,并且前者仅针对组合对象哈希值Attribute值首要的关键。

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

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

  2. 遍历所有返回的项目,并照常使用DeleteItem

    • 更新:最有可能的BatchWriteItem更适用于这样的用例(有关详细信息,请参见下文)。

更新资料

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

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

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

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

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

不过,对于手边的用例来说,这显然可以带来潜在的重大收益。


4
我认为将批处理删除用于第二步(将其“屏蔽”为批处理写操作)是
有意义的

1
@ivant-非常感谢您的提示,BatchWriteItem的这种“蒙版”删除功能确实使我逃脱了;我已经相应地更新了答案。
斯特芬·欧宝

用于删除BatchWriteItem项目的操作需要通过TableWriteItems
Neil


3
我意识到这已经很老了,OP并没有提到特定的语言SDK,但是在Python batch_writer()中,boto3.resource.TableAPI的一个高级部分是“ 将自动处理批量缓冲和发送项目。此外,批处理编写器还将还可以自动处理所有未处理的项目并根据需要重新发送”,即,它是BatchWriteItem的包装器,用于管理烦人的部分。boto3.amazonaws.com/v1/documentation/api/latest/reference/…–
达沃斯

46

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

见下文:

“删除整个表比逐个删除项要有效得多,因为与删除操作一样多的删除操作,实质上使写入吞吐量增加了一倍”

如果您只希望删除数据的一部分,则可以为每个月,每年或类似时间创建单独的表。这样,您可以删除“上个月”并保持其余数据不变。

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

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

8
我也很喜欢这个答案,但请注意:这可能会在您的系统中创建许多表,我们将按表付费。因此,在不删除该表的情况下,您需要在月底(如果您的表是每月)之后减少配置。
塞尔吉奥·MC·菲盖雷多2014年

2
同意这个答案,如果您需要删除表格中的所有记录,则适用此答案,但发问者此处要删除用户基本条目而不是整个表格。
Ihtsham Minhas 2015年

1
考虑到DynamoDB的定价,为每个用户使用单独的表表将非常昂贵。每月一张桌子实际上会使情况变得更糟。显然,这是针对另一个非常具体的问题的答案。
安德烈Werlang

11
如果您使用自动供应(例如CloudFormation)将表作为堆栈的一部分进行管理,则删除表可能也不是一个吸引人的选择。我不知道使CloudFormation重新创建您手动删除的表的简单方法。
brabster

2
这种方法需要相当多的时间来删除和重新创建表(需要时),从而使其在整个时间内都不可用。该问题明确指出要删除用户数据,这对于将每个用户表拆分成单独的表是不切实际的。
安德烈·沃朗(AndréWerlang)'17

13

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

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


添加TTL是一个“更新”(写操作)。我不确定执行“更新”而不是“删除”有什么好处。
Tomer

您可以使用原始写入方式插入该数据,并使用其他任何更新操作对其进行更新。当然,如果您有一堆数据然后想要删除它,则不是选择。但这是有效的选项,适用于您可以为插入或更新的数据提供ttl的情况。
Lukas Liesis '19

1
我同意,如果已经配置了TTL,并且清理最多可以等待48小时,那绝对是最佳选择。如果不清楚,我深表歉意。
Tomer

4

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

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

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

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


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控制台重新创建它可能会导致问题。


0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.