在亚马逊lambda上使用mysql池

Bor*_*ris 3 node.js aws-lambda

我正在尝试在我的运行在Amazon Lambda上的NodeJS服务中使用mysql池.这是我的模块的开始,它与数据库一起使用:

console.log('init database module ...');
var settings = require('./settings.json');
var mysql = require('mysql');
var pool = mysql.createPool(settings);
Run Code Online (Sandbox Code Playgroud)

如下面的亚马逊控制台中的日志,这段代码经常执行:

  1. 如果我只是部署了服务并同时执行了10个请求 - 所有这10个请求都执行这段代码.
  2. 如果我在第一次系列之后立即再次同时执行10个请求 - 它们不会执行此代码.
  3. 如果从最后一个查询传递了一些时间 - 那么一些请求会重新执行该代码.

即使我使用全局 - 这会减少但不会消除重复:

if (!global.pool) {
    console.log('init database module ...');
    var settings = require('./settings.json');
    var mysql = require('mysql');
    global.pool = mysql.createPool(settings);
}
Run Code Online (Sandbox Code Playgroud)

此外,如果请求执行有一些错误 - 这段代码在请求之后执行,global.pool在那时为空.

那么,这是否意味着在Amazon Lambda中使用池是不可能的?有什么选择我怎么能让亚马逊每次都使用相同的池实例?

Mic*_*bot 8

每次调用Lambda函数时,它都在自己的独立容器中运行.如果没有可用的空闲容器,则服务会自动创建一个新容器.因此:

  1. 如果我只是部署了服务并同时执行了10个请求 - 所有这10个请求都执行这段代码.

如果容器可用,它可能并且很可能会被重用.当发生这种情况时,进程已经在运行,因此全局部分不会再次运行 - 调用从处理程序开始.因此:

  1. 如果我在第一次系列之后立即再次同时执行10个请求 - 它们不会执行此代码.

每次调用完成后,使用的容器将被冻结,最终将被解冻并重新用于后续调用,或者如果在几分钟后不需要,则会被销毁.从而:

  1. 如果从最后一个查询传递了一些时间 - 那么一些请求会重新执行该代码.

现在有道理,对吧?

唯一的"问题"是在销毁容器之前必须经过的时间量不是固定值.有趣的是,它似乎是大约15分钟,但我不相信它是有记录的,因为很可能计时器是自适应的...服务可以(在它的自由裁量权时)使用启发式方法来预测最近的活动是否是尖峰或可能是持续,并可能考虑其他因素.

(Lambda @ Edge,它是与CloudFront集成的Lambda用于HTTP标头操作,似乎在不同的时间运行.空闲容器似乎持续更长时间,至少是少量,但这是有道理的,因为它们总是非常小的容器..而且这个观察结果再次轶事.)

代码的全局部分仅在创建新容器时运行.

池化没有意义,因为在调用期间没有任何共享 - 每次调用都是在其容器中运行的唯一调用 - 每个进程一次 - 在任何时候.

但是,您要做的是改变idle_timeout连接.MySQL服务器没有一种有效的方法来"发现"空闲连接完全消失,所以当容器被破坏时你的连接消失,服务器只是坐在那里,连接保持在Sleep状态,直到默认idle_timeout到期.默认值为28800秒,或8小时,这太长了.您可以在服务器上更改此设置,也可以发送查询SET @@IDLE_TIMEOUT = 900(尽管您需要尝试使用适当的值).

或者,您可以为每次调用建立和销毁处理程序内部的连接.当然,这会花费更多的时间,但如果你的功能不会经常运行,这是一种明智的方法.MySQL客户端/服务器协议的连接/握手序列相当轻量级,并且频繁的连接/断开不会对服务器造成如您所料的那样多的负载......尽管您不希望在使用的RDS服务器上执行此操作IAM令牌身份验证,这是更加资源密集型的.

  • @Jabari 确实如此。当您尝试从池中获取现有连接时,池应该自动检查它以验证它是否仍然存在,并且如果旧连接出现问题,池应该处理创建新连接的开销。当我写这个答案时,我并没有沿着这些思路思考,因为OP似乎对为什么池化连接不可用感到困惑,这更像是一个Lambda问题,但你是对的——池化仍然可以即使跨容器的请求不会共享池,也能提供优势。 (2认同)