【问题标题】:Batch write more than 25 items on DynamoDB using Lambda使用 Lambda 在 DynamoDB 上批量写入超过 25 个项目
【发布时间】:2020-02-19 23:45:43
【问题描述】:

Edit x1:将 sn-p 替换为完整文件

我目前正在 DynamoDB 中播种 1.8K 行。创建用户时,需要生成并插入这些行。它们不需要立即阅读(比方说,在不到 3 - 5 秒内)。我目前正在使用 AWS Lambda,但遇到了超时异常(可能是因为消耗的 WCU 多于预置的 WCU,我有 5 个禁用了 Auto-Scaling)。

我尝试在 Google 和 StackOverflow 周围进行搜索,这似乎是一个灰色区域(这有点奇怪,考虑到 DynamoDB 被宣传为每秒处理大量数据的令人难以置信的解决方案),其中不存在明确的路径.

我们知道 DynamoDB 限制每批 25 个项目的插入,以防止 HTTP 开销。这意味着我们可以调用无限数量的 batchWrite 并增加 WCU。

我尝试通过触发它而不等待它们来调用无限数量的batchWrite(这会算吗?我已经读过,由于JS是单线程的,所以无论如何都会一个一个地处理请求,除非我不会如果我不使用承诺,则不必等待响应......目前使用节点 10 和 Lambda),似乎什么都没有发生。如果我承诺调用并等待它,我会得到一个 Lambda 超时异常(可能是因为它用完了 WCU)。

我目前有 5 个 WCU 和 5 个 RCU(对于这些随机尖峰操作来说这些太小了吗?)。

我有点卡住了,因为我不想在短时间内随机增加 WCU。此外,我了解到 Auto-Scaling 不会自动启动,亚马逊每天只会调整容量单位 4 次。

我该怎么办?

这是我用来插入 DynamoDB 的完整文件

const aws = require("aws-sdk");

export async function batchWrite(
  data: {
    PutRequest: {
      Item: any;
    };
  }[]
) {
  const client = new aws.DynamoDB.DocumentClient({
    region: "us-east-2"
  });
  // 25 is the limit imposed by DynamoDB's batchWrite:
  // Member must have length less than or equal to 25.
  // This verifies whether the data is shaped correctly and has no duplicates.
  const sortKeyList: string[] = [];
  data.forEach((put, index) => {
    const item = put.PutRequest.Item;
    const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
    const hasPk = has.call(item, "pk");
    const hasSk = has.call(item, "sk");
    // Checks if it doesn't have a sort key. Unless it's a tenant object, which has
    // the accountType attribute.
    if (!hasPk || !hasSk) {
      throw `hasPk is ${hasPk} and hasSk is ${hasSk} at index ${index}`;
    }

    if (typeof item["pk"] !== "string" || typeof item["sk"] !== "string") {
      throw `Item at index ${index} pk or sk is not a string`;
    }

    if (sortKeyList.indexOf(item.sk) !== -1) {
      throw `The item @ index ${index} and sortkey ${item.sk} has duplicate values`;
    }

    if (item.sk.indexOf("undefined") !== -1) {
      throw `There's an undefined in the sortkey ${index} and ${item.sk}`;
    }

    sortKeyList.push(put.PutRequest.Item.sk);
  });

  // DynamoDB only accepts 25 items at a time.
  for (let i = 0; i < data.length; i += 25) {
    const upperLimit = Math.min(i + 25, data.length);
    const newItems = data.slice(i, upperLimit);
    try {
      await client
        .batchWrite({
          RequestItems: {
            schon: newItems
          }
        })
        .promise();
    } catch (e) {
      console.log("Total Batches: " + Math.ceil(data.length / 25));
      console.error("There was an error while processing the request");
      console.log(e.message);
      console.log("Total data to insert", data.length);
      console.log("New items is", newItems);
      console.log("index is ", i);
      console.log("top index is", upperLimit);
      break;
    }
  }
  console.log(
    "If no errors are shown, creation in DynamoDB has been successful"
  );
}

【问题讨论】:

  • 拆分它们并将它们发送到 SQS,然后有一个 lambda 触发器。这将允许一些 lambda 并发
  • @LostJon 您是指 Lambda 到 SQS,然后是 Lambda 到 DynamoDB?
  • 是的!您将有一个 lambda 来将消息分发到 SQS,然后是一个由 SQS 消息触发的 lambda,类似于您在此处的内容,以写入 DDB

标签: javascript node.js aws-lambda amazon-dynamodb


【解决方案1】:

您面临两个问题,但我会尝试解决它们。

尚未提供正在编写的项目的完整示例以及带有所示项目的实际batchWrite 请求,因此不清楚实际请求的格式是否正确。根据提供的信息和面临的问题,请求的格式似乎不正确。

AWS Javascript SDK 中batchWrite 操作的文档可以在here 找到,之前的答案here 显示了正确构建和格式化batchWrite 请求的解决方案。

尽管如此,即使请求格式正确,仍然存在第二个问题,即提供了足够的容量来处理写入请求,以便在要求的时间量(上限为 5 秒)内插入 1800 条记录.

TL;DR 容量问题的快速简便的解决方案是从 Provisioned Capacity 切换到 On Demand em>容量。如下所示,数学表明,除非您有一致和/或可预测的容量要求,否则大部分时间按需容量不仅会消除预置容量的管理开销,而且还会消除会便宜很多。

根据预置容量 here 的 AWS DynamoDB 文档,Write Capacity Unit 或 WCU 被计费,因此定义如下:

将数据写入表的每个 API 调用都是一个写入请求。对于大小不超过 1 KB 的项目,一个 WCU 每秒可以执行一个标准写入请求。

batchWrite / batchWriteItem API here 的 AWS 文档表明,batchWrite API 请求支持每个请求最多 25 个项目,单个项目最大可达 400kb。此外,处理batchWrite 请求所需的 WCU 数量取决于请求中项目的大小。用于管理 DynamoDB here 中的容量的 AWS 文档建议处理 batchWrite 请求所需的 WCU 数量计算如下:

BatchWriteItem — 将最多 25 个项目写入一个或多个表。 DynamoDB 将批次中的每个项目作为单独的 PutItem 或 DeleteItem 请求处理(不支持更新)。因此 DynamoDB 首先将每个项目的大小向上舍入到下一个 1 KB 边界,然后计算总大小。结果不一定与所有项目的总大小相同。例如,如果 BatchWriteItem 写入一个 500 字节的项目和一个 3.5 KB 的项目,DynamoDB 会将大小计算为 5 KB (1 KB + 4 KB),而不是 4 KB (500 字节 + 3.5 KB)。

batchWrite 请求中项目的大小尚未提供,但为了回答这个问题,假设它们每个小于 1KB。请求中有 25 个每项 每秒处理单个 batchWrite 请求。假设提供了最少 25 个所需的 WCU,考虑到插入项目的 5 秒时间限制,仅提供 25 个 WCU,每秒只能发出一个具有 25 个项目的请求,在 5 秒的时间限制内总共插入 125 个项目。基于此,为了实现在 5 秒内插入 1800 个项目的目标,需要 360 个 WCU 来实现该目标。

根据here 发现的预置容量的当前定价,360 个 WCU 的预置容量的成本约为每月 175 美元(不考虑免费层积分)。

您可以通过两种方式处理此问题

  1. 增加预置容量。要在 5 秒内完成 1800 个项目,您需要配置 360 个 WCU。
  2. 更好的选择是简单地切换到On Demand 容量。问题提到写请求是“随机尖峰操作”。如果写入请求不是对表的可预测且一致的操作,那么结果通常是过度配置表并为空闲容量付费。 “按需”容量解决了这个问题,并坚持无服务器理念,即只为你使用的东西付费,而你只为你消费的东西付费。目前,按需定价为 1.25 美元/100 万个 WCU 消耗量。基于此,如果每个新用户生成 1800 个要插入的新项目,则每月需要创建 97,223 个新用户,然后表的预置容量与使用按需容量相比具有竞争力。换句话说,在平均每 26 秒注册一个新用户之前,数学建议坚持按需容量(值得注意的是,这不考虑 RCU 或表中的其他项目或其他访问模式)。李>

【讨论】:

  • 感谢您的回答!我已经更新了代码以更好地反映我是如何解决这个问题的。我已经用整个文件替换了 sn-p。如图所示,我一直将对象放置在 PutItem 对象中。
  • 我想花更多时间感谢您的详细回复。感谢您提供链接并花时间回答我的问题!
  • 我已将选项更改为 On Demand,现在 lambda 在大约 8 秒内播种它?
猜你喜欢
  • 2015-09-12
  • 1970-01-01
  • 1970-01-01
  • 2020-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-16
  • 2016-12-07
相关资源
最近更新 更多