【问题标题】:Reduce DynamoDB latency from Java Lambda减少 Java Lambda 的 DynamoDB 延迟
【发布时间】:2018-10-31 13:33:43
【问题描述】:

如何减少我目前在 Lambda 和 DynamoDB 之间看到的延迟?

它是一个 Java lambda,使用 AWS 提供的 SDK 执行 2 个 DynamoDB 操作中的每一个都需要 4 秒。我听说这些通常在 完全空的表执行表扫描(代码如下)。

我应该怎么做才能减少延迟?

我尝试过的事情

  • lambda 和 DynamoDB 位于同一区域 (eu-west-1)。
  • 该表有 5 个 RCU 和 WCU。增加这些也无济于事。
  • lambda 使用的最大内存为 92MB。如果我分配最小的 128MB,那么它会在 15 秒后超时。将内存增加到 512MB 可实现每次调用 4 秒的时间,再次增加到 1GB 可将每次调用减少到 2 秒。然而,对于一个微不足道的 lambda,这是一个荒谬的内存量,仍然让我的预期延迟超过 200 倍。
  • 表指标显示表扫描时间在 12 毫秒到 15 毫秒之间。这是我所期待的。即使考虑到建立网络连接的开销,我仍然预计需要 10 毫秒,而不是几秒。
  • 我正在使用 AWS 控制台中的测试功能触发 lambda。我也尝试过通过 API Gateway 触发(我最终会这样做),结果相同。
  • 我已尝试快速连续多次调用 lambda(以减少我遭受设置成本的可能性)。它没有帮助。
  • 日志记录显示 lambda 的所有其他部分运行得非常快(毫秒)。

代码 sn-ps

创建 DynamoDB 对象

log("Creating AmazonDynamoDB");
AmazonDynamoDB db = AmazonDynamoDBClientBuilder                        
  .standard()
  .withRegion(Regions.EU_WEST_1)
  .build();

log("Creating DynamoDBMapper");
DynamoDBMapper mapper = new DynamoDBMapper(db);

执行扫描

log("Scanning table");
List<MyItem> items = dbMapper.scan(MyTable.class, new DynamoDBScanExpression());
for (MyItem item : items) {
        // Irrelevant - there aren't any
}
log("Table scan complete");

示例日志

这里是运行的日志。

20:07:41 START RequestId: 9d436db7-5d32-11e8-8555-8564d2094ccc Version: $LATEST
20:07:41 Received request: APIGatewayRequest(path=/data/foo, httpMethod=POST, body=)
20:07:41 Creating AmazonDynamoDB
20:07:45 Creating DynamoDBMapper
20:07:45 Creating DataHandler
20:07:45 Handling request
20:07:45 Scanning table
20:07:49 Table scan complete
20:07:49 Request handled - response object: []
20:07:49 APIGatewayResponse(isBase64Encoded=false, statusCode=200, body=[], headers={})
20:07:49 END RequestId: 9d436db7-5d32-11e8-8555-8564d2094ccc
20:07:49 REPORT RequestId: 9d436db7-5d32-11e8-8555-8564d2094ccc Duration: 8256.47 ms Billed Duration: 8300 ms Memory Size: 512 MB Max Memory Used: 85 MB

【问题讨论】:

  • 你明白内存和可用 CPU 周期在 lambda 中是线性耦合的,对吧? 128 MiB RAM = 1 个内核的 1/8。 512 MiB = 1 个核心的 1/2。 1 GiB = 1 个内核。这不仅与内存有关,还与您的容器可以访问的机器的一部分有关。您还需要在处理程序之外初始化 Dynamo 对象,这样您就不必在每次调用时都这样做,而且从显示的有限来源中不清楚您是否已经这样做了。
  • 我在使用 Java 时也遇到过类似的情况。我尝试使用 Javascript/NodeJS 并且我的 lambda 运行得更快。如果您进行比较,我很乐意看到您的输出。
  • 明确地说,我怀疑您的网络延迟可以忽略不计。我想您可能会发现加载 DynamoDB Java SDK 大约需要 4 秒。
  • 有趣的@Stu。我将尝试将“创建 DynamoDB 对象”sn-p 移回静态初始化程序,以查看它是否使后续请求更好。我不是很有希望,因为表扫描也很慢,但值得一试。我还会看看我是否可以将某些东西放在一起,以便其他运行时进行比较。

标签: java performance aws-lambda amazon-dynamodb


【解决方案1】:

根据 AWS 论坛上一位 AWS 员工的 this post 所说,AmazonDynamoDB 对象的构建成本很高。将构造(返回)移动到静态初始化程序中并结合一点额外的内存(=CPU)分配基本上可以解决问题。

日志中的数据仍然显示,上述 2 个缓慢步骤中的每一个都需要大约一半的时间。因此,AmazonDynamoDB 对象的构造和首次使用可能都很慢。

显然这对第一个请求没有帮助,它仍然需要与问题相同的时间。但是,一旦 lambda 升温,后续请求大约需要 15 毫秒(远低于 100 毫秒的最低计费阈值)。解决第一个请求问题是well understood - 例如通过使用 CloudWatch Events 安排对 lambda 的定期调用以使其保持温暖。

2020年编辑:也可以使用Provisioned Currency处理冷启动问题。

【讨论】:

    【解决方案2】:

    (不是答案,但我希望对其他人有所帮助) 我已经在此处发布了更新,除此之外,我还必须对 dynamoDb 进行“虚拟”查询操作(以打开与它的连接),以防万一帮助其他人,我的代码如下:

    class MyFunctionHandler : RequestHandler<Map<String, Any>, ApiGatewayResponse> {
    
    //var dbClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder.defaultClient()
    var dbClient: AmazonDynamoDB = AmazonDynamoDBClientBuilder
            .standard().withRegion("sa-east-1").build()
    
    override fun handleRequest(input: Map<String, Any>, context: Context): ApiGatewayResponse {
        LOG.info("received input: $input")
    
        input["wakeup"]?.let {
    
            if (it == true) {
    
                with(EmpresaRepository(dbClient)) {
                    LOG.info("### Connection was not stablished at this point")
    
                    someDynamoQueryHere("dummyParameter")
    
                    LOG.info("### The Connection was opened and will keep alived for 1 minute")
                }
    
                return buildResponseForWakeUpReq(input)
            }
        }
    
        val param = input["queryStringParameters"]?.toString()
    ...
    

    随后会打开 dynamoDb 连接的操作将以毫秒为单位发生!

    【讨论】:

    【解决方案3】:

    所有 aws ClientBuilder.build() 函数在 lambda 中都需要一段时间,并且依赖于函数专用的内存。但是,如果容器已经初始化并且您正在第二次或在后续请求中调用 ClientBuilder.build(),它会以毫秒而不是秒为单位发生。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-10
      • 2019-11-08
      • 2019-12-12
      相关资源
      最近更新 更多