我正在DynamoDB中编写一个简单的日志记录服务。
我有一个日志表,该表由user_id哈希和时间戳(Unix epoch int)范围键入。
当服务的用户终止其帐户时,无论范围值如何,我都需要删除表格中的所有项目。
进行此类操作的推荐方法是什么(请记住可能有数百万项要删除)?
据我所知,我的选择是:
答:执行扫描操作,在每个返回的项目上调用删除,直到没有项目为止
B:执行BatchGet操作,再次对每个项目调用delete,直到没有剩下
这些对我来说都很糟糕,因为它们需要很长时间。
我理想的做法是调用LogTable.DeleteItem(user_id) - 不提供范围,并让它为我删除所有内容。
答案 0 :(得分:43)
我理想的做法是调用LogTable.DeleteItem(user_id) - 没有提供范围,并让它为我删除所有内容。
确实可以理解的要求;我可以想象这些高级操作可能会随着时间的推移而被AWS团队添加(他们有首先从有限的功能集开始并根据客户反馈评估扩展的历史),但这是你应该做的,以避免成本至少完整扫描:
使用Query而不是Scan来检索user_id
的所有项目 - 无论使用何种组合哈希/范围主键,这都有效,因为 HashKeyValue < / em>和 RangeKeyCondition 是此API中的单独参数,前者仅针对复合主键的哈希组件的属性值。。
要从中继续先前查询的项的主键。一个 如果更早,查询可能会将此值提供为LastEvaluatedKey 查询操作在完成查询之前被中断;或 因为结果集大小或Limit参数。该 LastEvaluatedKey可以在新的查询请求中传回以继续 从那时起的操作。
循环播放所有返回的项目,并照常提供DeleteItem
正如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的所有项目,然后遍历所有返回的项目,并促进DeleteItem
或BatchWriteItem
。但请记住,这里可能会消耗大量的吞吐量。例如,考虑一种情况,您需要从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;
};