如果条目已存在,如何防止覆盖 DynamoDB 项目
Posted
技术标签:
【中文标题】如果条目已存在,如何防止覆盖 DynamoDB 项目【英文标题】:How to prevent a DynamoDB item being overwritten if an entry already exists 【发布时间】:2018-03-13 20:49:56 【问题描述】:我正在尝试编写一个 lambda 函数来将新数据添加到 DynamoDB 表中。 从阅读文档:
http://docs.aws.amazon.com/AWSjavascriptSDK/latest/AWS/DynamoDB/DocumentClient.html#put-property PUT 方法:“创建新项目,或通过委托给 AWS.DynamoDB.putItem() 将旧项目替换为新项目。”
除了在“放置”之前检查对象之外,是否有设置或标志在尝试 PUT 时使对象存在失败?
我可以看到
params -> Expected -> Exists (Bool)
但看不到任何有关此功能的文档。
防止项目覆盖的最佳架构(或禁食)是什么?
Query the table first and if no item exists then add the item
或
Attempt to insert the item and on failure because of duplicate entry report this back? (Is there a way to prevent item overwrite?)
【问题讨论】:
【参考方案1】:ConditionExpression
可用于检查表中是否已存在键属性值,仅当表中不存在键值时才执行PUT操作。
当你运行下面的代码时,第一次 put 操作应该是成功的。在第二次运行中,put 操作应该会失败并出现 "Conditional request failed" 异常。
我的电影表同时具有分区键和排序键。所以,我在条件表达式中使用了这两个属性。
带条件放置的示例代码:-
var table = "Movies";
var year = 1502;
var title = "The Big New Movie";
var params =
TableName:table,
Item:
"yearkey": year,
"title": title,
"info":
"plot": "Nothing happens at all.",
"rating": 0
,
ConditionExpression: "yearkey <> :yearKeyVal AND #title <> :title",
ExpressionAttributeNames:
"#title" : "title"
,
ExpressionAttributeValues:
":yearKeyVal" : year,
":title": "S": title
;
console.log("Adding a new item...");
docClient.put(params, function(err, data)
if (err)
console.error("Unable to add item. Error JSON:", JSON.stringify(err, null, 2));
else
console.log("Added item:", JSON.stringify(data, null, 2));
);
第二次执行put操作时出现异常:-
Unable to add item. Error JSON:
"message": "The conditional request failed",
"code": "ConditionalCheckFailedException",
"time": "2017-10-02T18:26:26.093Z",
"requestId": "7ae3b0c4-3872-478d-908c-94bc9492a43a",
"statusCode": 400,
"retryable": false,
"retryDelay": 0
【讨论】:
太棒了,非常感谢您的信息和帮助! 只是一个警告(因为这花了我几个小时才找到):这种方法不适用于批量请求!似乎条件仅适用于单个 PutItem 请求。 我不明白为什么在“ConditionExpression”中使用“AND”而不是“OR”,因为两个项目分开放置,例如 ('A', 'A') 和 ('A', ' B') 将看起来失败,因为第一个字段失败但应该被接受。但是,事实是无论使用“AND”还是“OR”,结果都是一样的:“一旦两个属性不匹配就会通过”。 @Chumicat,实际上,只检查yearkey <> :yearKeyVal
而不使用 AND 或 OR 就足够了。这个ConditionExpression
只会针对已经存在的项目进行检查,通过具有相同的主键来标识(如果我们打算更新此类项目而不是添加新项目,这将是可以的)。这意味着如果 Dynamo 找到一个现有行来检查此条件,我们已经确定我们希望条件失败,甚至无需查看其内容。因此,在此类数据上微不足道的任何谓词都将起作用。我在下面添加了我自己的答案作为另一个这样的例子。
@spaceemotion, conditionCheck
确实不能在BatchWriteItem
上使用,但它们可以与TransactWriteItems
一起使用。在这种情况下,行为是如果任何检查失败,则取消整个事务。如果在TransactWriteItems
中表达了多个条件并且其中一些条件失败,则它们的成功/失败状态将按照与请求中相同的顺序在响应中提供,可用于解释正在发生的事情。【参考方案2】:
我看到这个问题与 JavaScript 语言有关,无论如何我也会为 Java 编写(也许它对某人有用):
DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression();
Map<String, ExpectedAttributeValue> expectedAttributes =
ImmutableMapParameter.<String, ExpectedAttributeValue>builder()
.put("hashKeyAttrName", new ExpectedAttributeValue(false))
.put("rangeKeyAttrName", new ExpectedAttributeValue(false))
.build();
saveExpression.setExpected(expectedAttributes);
saveExpression.setConditionalOperator(ConditionalOperator.AND);
try
dynamoDBMapper.save(item, saveExpression);
catch (ConditionalCheckFailedException e)
e.printStackTrace();
ConditionalCheckFailedException
将被抛出,以防我们尝试使用 DynamoDB 中已经存在的 hashKey 和 rangeKey 对保存项目。
【讨论】:
多么可怕的语言 为什么可怕? :) 这是一个习惯问题,Java 是一种流行的编程语言) 看看,就是丑。我喜欢类型化语言,但我觉得在 Java 开发过程中应该有人在某处说停止!【参考方案3】:作为对 notionquest 正确答案的补充,在项目已经存在的情况下,表达使 putItem
失败的条件的推荐方法是断言分区键不存在于任何潜在存在的项目中:
"ConditionExpression": "attribute_not_exists(pk)",
这读作“如果此项目在putItem
之前已经存在,请确保它还没有分区键”(pk
是本示例中的分区键)。由于没有分区键就不可能存在项目,这实际上意味着“确保该项目不存在”。
另请参阅本页开头的“注释”块:https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dynamodb/put-item.html
这里有一个例子用来保证非键列的唯一性:
https://aws.amazon.com/blogs/database/simulating-amazon-dynamodb-unique-constraints-using-transactions/
【讨论】:
以上是关于如果条目已存在,如何防止覆盖 DynamoDB 项目的主要内容,如果未能解决你的问题,请参考以下文章
如何检查然后更新node.js中的AWS dynamoDB项?