有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

性能降低了Java Lambda的DynamoDB延迟

如何减少Lambda和;发电机

这是一个Java lambda,使用AWS提供的SDK执行2个DynamoDB操作需要4个小时。我听说这些通常在<;20毫秒,但对我来说要高出3个数量级。这些长操作包括(a)创建DynamoDB对象和(b)在完全空的表上执行表扫描(代码如下)

我应该做些什么来减少延迟

我尝试过的东西

  • lambda和DynamoDB均位于同一区域(eu-west-1)
  • 表中有5个RCU和;WCU。增加这些并没有帮助
  • lambda使用的最大内存为92MB。如果我分配最小128MB,那么它将在15秒后超时。将内存增加到512MB可实现每次呼叫4秒的计时,再次将内存增加到1GB可将每次呼叫的计时减少到2秒。然而,对于一个微不足道的lambda来说,这是一个荒谬的记忆量,仍然留给我>;预期延迟的200倍
  • 表度量显示表扫描时间在12到15毫秒之间。这就是我所期待的。即使有建立网络连接的开销,我仍然期望只有10毫秒,而不是几秒钟
  • 我正在使用AWS控制台中的测试功能触发lambda。我还尝试过通过API网关触发(这是我最终将要做的),结果也是一样的
  • 我已经连续几次尝试给lambda打电话(以减少我遭受安装成本的可能性)。这没用
  • 日志记录显示lambda的所有其他部分运行得非常快(毫秒)

代码片段

创建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

共 (3) 个答案

  1. # 1 楼答案

    (不是答案,但我希望它能帮助其他人) 我已经在这里发布了更新,除此之外,我还必须在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连接的后续操作将以毫秒为单位

  2. # 2 楼答案

    根据AWS论坛上一位AWS员工的this post,构建AmazonDynamoDB对象的成本很高。将构造(返回)移到静态初始值设定项中,再加上一点额外的内存(=CPU)分配,基本上可以解决问题

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

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

    2020年编辑:您还可以使用Provisioned Currency来处理冷启动问题

  3. # 3 楼答案

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