具有多个查询的 NodeJS mysql 连接池

Rya*_*yan 2 mysql asynchronous connection-pooling node.js

我对 NodeJS 相当陌生(来自 PHP 背景)。与 PHP 的阻塞性质相比,我对 NodeJS 的异步性质有基本的了解。我创建了一些连接到 mysql 的应用程序节点并让它们正常工作。

我目前对在 Node 中执行查询的最佳方式的理解是:

  • 创建连接池
  • 当您需要执行查询时:
  • 从池中获取连接()
  • 执行查询
  • .release() 连接

我处于需要迭代大量项目并将每个项目插入数据库的情况。执行上述操作无效。我的想法是,由于每个查询都是并行执行的,列表中的下一项正在尝试获取连接,但由于之前的查询可能尚未完成,因此没有可用的。没有抛出任何错误,只是似乎没有放入项目。通过日志记录,我发现它从未达到运行 pool.getConnection() 回调的程度。

所以我我可以修改连接流程:

  • 创建连接池
  • 当您需要执行多个查询时:
  • 从池中获取连接()
  • 迭代需要查询的项目
  • 执行查询
  • .release() 连接

问题是,我怎么知道 .release() 我的连接什么时候是安全的?我不能只在我的 .query() 回调中执行此操作,因为之后可能会有更多查询。但我也不能在查询回调中执行此操作,因为我知道我需要等待所有查询完成。我是否负责创建自己的逻辑来确定所有查询(以及所有可能的未来查询,即使它们尚未启动)何时完成?或者这只是没有完成的事情?

在 PHP 中,这将是微不足道的,因为它具有阻塞性质。我选择在这个应用程序中使用 NodeJS,因为它有时确实需要异步。但它在我的应用程序不需要的地方强制我使用异步(在某些情况下不应该使用它)。

我不愿发布我的代码,因为它有点复杂,所以我不想分散这个特定主题的注意力。

Eve*_*ert 10

您的连接流程很好。如果您只执行 1 个查询,则实际上不需要执行任何步骤,您只需调用.query()池。(引擎盖下的其余部分)。

您需要在 1 个事务中执行多个查询的情况主要是,但我会添加事务支持:

  • 创建连接池
  • 当您需要执行多个查询时:
  • 从池中获取连接()
  • 开始交易
  • 迭代需要查询的项目
  • 执行查询
  • 如果有任何失败,停止并回滚
  • 如果全部成功,提交
  • .release() 连接

如果所有查询都“完成”,您就知道何时释放连接。你怎么知道完成了?方法不止一种。我建议您使用 promise API,因此它可能如下所示:

async function doStuff(items) {

  try { 
    const connection = await pool.getConnection();
    await connection.beginTransaction();
    for(const item of items) {
      await connection.query('....');
    }
    await connection.commit();
  } catch (e) {
    await connection.rollback();
    throw e;
  } finally {
    await connection.release();
  }

}
Run Code Online (Sandbox Code Playgroud)

这种模式有几个优点:

  1. 它会正确报告错误
  2. 它将在成功和错误时释放连接。
  3. 它要么完全失败,要么完全成功。这里没有半途而废。

如果你不关心事务,这可以简化:

async function doStuff(items) {

  try { 
    const connection = await pool.getConnection();
    for(const item of items) {
      await connection.query('....');
    }
  } finally {
    await connection.release();
  }

}
Run Code Online (Sandbox Code Playgroud)

这样做的问题是您可以获得部分成功,这通常是不可取的,尤其是对于 API。但是,如果它对您来说足够好,那就足够了。

如果你对没有交易感到舒服,理论上你可以完全跳过这getConnection一步:

async function doStuff(items) {
  for(const item of items) {
    await pool.query('....');
  }
}
Run Code Online (Sandbox Code Playgroud)

这意味着您的所有查询都可能在不同的连接上执行。这可能会给您带来更差的性能,但会使代码更简单。

如何启用承诺?

嗯,这有点争议。为此,您可能需要切换 mysql 包。有一个mysql2包的mysql2/promise导入非常好(并且实际上与更流行的mysql包共享代码)。超级推荐切换到这个。

如果我不想切换套餐怎么办?

好吧,这个回调版本要痛苦得多。在这种情况下,我建议您仍然使用承诺,但将您的回调转换为承诺模式,也许使用“promisify”。

如果你真的不想在任何地方使用 promise,你基本上必须将我的示例转换为基于回调的示例,这看起来会更痛苦。