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

时间:2019-01-02 18:39:18

标签: mysql node.js asynchronous connection-pooling

我刚接触NodeJS(从PHP背景开始)。与PHP的阻塞性质相比,我对NodeJS的异步性质具有基本的了解。我创建了一些连接到mysql的应用程序节点,并使这些节点正常工作。

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

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

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

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

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

与此相关的问题是,如何知道何时安全地释放.connection()我的连接?我不能只在我的.query()回调函数中执行此操作,因为它之后可能会有更多查询。但是我也不能在查询回调中做到这一点,因为我知道我需要等待所有查询完成。我是否要负责创建自己的逻辑,以弄清楚何时完成所有查询(以及所有可能的将来查询,即使尚未开始)?还是这只是未完成的事情?

在PHP中,这是很简单的,因为它具有阻塞性。我选择为此应用程序使用NodeJS,因为它有时有时需要异步。但是它在我的应用不需要异步的地方(在某些情况下不应该使用它)迫使我产生异步性。

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

2 个答案:

答案 0 :(得分:3)

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

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

  • 创建连接池
  • 当您需要执行许多查询时:
  • 从池中获取getConnection()
  • 开始交易
  • 重复需要查询的项目
  • 执行查询
  • 如果有任何失败,请停止并回滚
  • 如果全部成功,则提交
  • .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();
  }

}

此模式有一些优点:

  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();
  }

}

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

如果您不愿意进行任何交易,从理论上讲,您可以完全跳过getConnection步骤:

async function doStuff(items) {
  for(const item of items) {
    await pool.query('....');
  }
}

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

如何启用承诺?

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

如果我不想切换软件包怎么办?

嗯,这的回调版本要痛苦得多。在这种情况下,我建议仍然使用Promise,但可以将回调转换为Promise模式,也许使用“ promisify”。

如果您真的不希望在任何地方做出承诺,则基本上必须将我的示例转换为基于回调的示例,这看起来会更加痛苦。

答案 1 :(得分:-1)

遍历项目并插入数据库是一个糟糕的选择!循环构建对象数组,然后使用批量插入功能一次性插入所有项目。这样,您只需调用一次getConnection(),然后在批量插入后调用.release()。

如果不是这种情况,那么在交互过程中,您需要致电

1)getConnection() 2)执行查询 3)调用.release()

对所有项目重复步骤1到步骤3。

当是最后一次迭代时,您需要调用callback()函数以将响应返回给调用函数。