【问题标题】:How to update multiple items in a DynamoDB table at once如何一次更新 DynamoDB 表中的多个项目
【发布时间】:2022-05-06 14:48:34
【问题描述】:

我正在使用 DynamoDB,我需要更新多条记录的特定属性。用伪语言编写我的要求我想做一个更新,上面写着“更新表 persons set relationshipStatus = 'married' where personKey IN (key1, key2, key3, ...)”(假设那 personKey 是我的 DynamoDB 表中的 KEY)。

换句话说,我想使用 IN 子句进行更新,或者我想可以将其称为批量更新。我发现this 链接明确询问是否存在像批量更新这样的操作,答案是不存在。但是,它没有提到 IN 子句。 documentation 表明 ConditionalExpressions 支持 IN 子句(一次可以提供 100 个值)。但是,我不确定这样的 IN 子句是否适合我的情况,因为我仍然需要提供一个强制性的 KEY 属性(它似乎需要一个值 - 我可能是错的)而且我担心它会做每次更新的全表扫描。

所以我的问题是:如何同时更新多个 DynamoDB 记录?目前,我几乎必须为每个 Key 一个一个地调用一个更新语句,这感觉真的不对……

【问题讨论】:

    标签: amazon-web-services amazon-dynamodb nosql


    【解决方案1】:

    如您所述,DynamoDB 不支持批量更新操作。您需要查询并获取要更新的所有记录的键。然后遍历该列表,一次更新每一项。

    【讨论】:

    • 我可以使用 for 循环并将我的主 ID 作为键来更新多条记录吗?
    • @prakashtank 您的问题与我在回答中所说的有何不同?您是否尝试过此操作并遇到特定问题?
    • 感谢您的回复。我不确定在 for 循环中是否会更新多个项目。但它确实有效。
    • @prakash tank 那么您能否在查询中使用 IN 或者您只是在循环中进行了多次更新?
    • @Forest :我在循环中更新了我的值。我没有尝试过,因为我没有找到太多。
    【解决方案2】:

    您可以使用 TransactWriteItems 操作更新 DynamoDB 表中的多条记录。

    官方文档可用here,也可以看TransactWriteItems javascript/nodejs 例子here

    【讨论】:

    • 嗨,我正在尝试解决同样的问题并考虑使用 TransactWriteItems。但我很困惑。事务写入可以同时更新多条记录,还是可以对一条记录执行多项操作?比如添加、获取、更新等。我无法从文档中弄清楚。提前致谢
    • 您好,TransactWriteItems 是一种同步写入操作,最多可将 25 个操作请求分组。
    【解决方案3】:

    我不知道自从给出答案后它是否发生了变化,但现在有可能

    查看文档: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html

    我在 javascript 中这样使用它(将新块映射到具有所需结构的对象数组:

    let params = {}
    let tableName = 'Blocks';
    
    params.RequestItems[tableName] = _.map(newBlocks, block => {
        return {
            PutRequest: {
                Item: {
                    'org_id': orgId,
                    'block_id': block.block_id,
                    'block_text': block.block_text
                },
                ConditionExpression: 'org_id <> :orgId AND block_id <> :block_id',
                ExpressionAttributeValues: {
                    ':orgId': orgId,
                    ':block_id': block.block_id
                }
            }
        }
    })
    
    docClient.batchWrite(params, function(err, data) {
       .... and do stuff with the result
    

    您甚至可以混合使用putsdeletes

    如果您使用 dynogels(由于 dynogels 支持,您不能混合 em,但您可以做的是更新(使用 create,因为它在幕后将它作为 put 的 batchWrite 函数)

    var item1 = {email: 'foo1@example.com', name: 'Foo 1', age: 10};
    var item2 = {email: 'foo2@example.com', name: 'Foo 2', age: 20};
    var item3 = {email: 'foo3@example.com', name: 'Foo 3', age: 30};
    
    Account.create([item1, item2, item3], function (err, acccounts) {
      console.log('created 3 accounts in DynamoDB', accounts);
    });
    

    请注意 DynamoDB 限制 (from the docs):

    BatchWriteItem 操作在一个或多个表中放置或删除多个项目。对 BatchWriteItem 的一次调用可以写入多达 16 MB 的数据,其中可以包含多达 25 个放置或删除请求。要写入的单个项目可以大到 400 KB。

    如果我没记错的话,我认为 dynogels 在发送请求之前将请求分成 25 个块,然后将它们收集在一个承诺中并返回 (尽管我对此不是 100% 确定) - 否则包装函数很容易组装

    【讨论】:

    • 如果您可以删除 put 请求中的数据,则此方法有效。批量写入不会更新项目,因此与现有 ID 匹配的任何对象都将完全替换现有 ID。在覆盖不正确的情况下,更新将需要发生......可能在一个循环中
    • 我想进一步澄清上述评论。上面的示例是一个 PUTRequest。 PUT 旨在创建/设置特定键的值(POST 将值创建为键的子项)。此示例不是关于更新记录/文档中的属性,而是关于键的批量 PUT。这就是为什么“覆盖记录”而不是“更新记录内容”的原因。对于我们这些来自 T-SQL/关系查询世界的人来说,PUT 更像是 INSERT 而不是 UPDATE 因为 INSERT 意图影响整个行,而 UPDATE 旨在更改“行”的一个或多个“列”。
    • @rainabba 只要您不使用补丁请求,这很有用,但在执行完整数据 obj 帖子时,应该 1 比 1 完成
    【解决方案4】:

    DynamoDb 并非设计为支持本机事务的关系数据库。最好首先设计模式以避免多次更新的情况。或者,如果在您的情况下不实用,请记住您可以在重构设计时对其进行改进。

    同时更新多个项目的唯一方法是使用 DynamoDB 提供的 TransactionWrite 操作。但它有一个限制(例如最多 25 个)。所以请记住,您可能也应该在您的应用程序中做一些限制。尽管成本非常高(因为实现涉及一些共识算法),但它仍然比简单的循环快。它为您提供了 ACID 属性,这可能是我们最需要的。想想使用循环的情况,如果其中一个更新失败,你如何处理失败?是否可以回滚所有更改而不会导致某些竞争条件?更新是幂等的吗?这实际上取决于您应用原因的性质。小心点。

    另一个选择是使用线程池来做网络 I/O 工作,这绝对可以节省很多时间,但它也有同样的失败和回滚问题要考虑。

    【讨论】:

      【解决方案5】:

      您可以按如下方式更新 aws dynamo 表中的多个项目。 假设您有一个包含 2 列的表格

      |第一列名 |第二列名|
      |列值 |列值  |

      Aws::String updateExpression("SET #columnA = :valueA, #columnB = :valueB");
      updateItemRequest.SetUpdateExpression(updateExpression);
      
      expressionAttributeNames["#columnA"] = "FirstColumnName";
      expressionAttributeNames["#columnB"] = "SecondColumnName";
      
      TArray<Aws::DynamoDB::Model::AttributeValue> attributeValues;
      Aws::DynamoDB::Model::AttributeValue avColumn1;
      avColumn1.SetS("ColumnValue1")
      attributeValues.Add(avColumn1);
      
      Aws::DynamoDB::Model::AttributeValue avColumn2;
      avColumn2.SetS("ColumnValue2")
      attributeValues.Add(avColumn2);
      
      Aws::Map<Aws::String, Aws::DynamoDB::Model::AttributeValue> expressionAttributeValues;
      expressionAttributeValues[":valueA"] = avColumn1;
      expressionAttributeValues[":valueB"] = avColumn2;
      const Aws::DynamoDB::Model::UpdateItemOutcome& result = Client->UpdateItem(updateItemRequest);
      if (!result.IsSuccess())
      {
          // sad face
      }
      
      

      【讨论】:

        猜你喜欢
        • 2022-12-10
        • 2022-09-23
        • 1970-01-01
        • 2019-01-08
        • 1970-01-01
        • 2021-01-24
        • 1970-01-01
        • 2016-02-24
        • 1970-01-01
        相关资源
        最近更新 更多