【问题标题】:NodeJS mysql connection pool with multiple queries具有多个查询的NodeJS mysql连接池
【发布时间】:2019-05-29 09:45:19
【问题描述】:

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

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

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

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

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

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

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

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

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

【问题讨论】:

    标签: mysql node.js asynchronous connection-pooling


    【解决方案1】:

    您的连接流程很好。如果您只进行 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();
      }
    
    }
    

    这种模式有几个优点:

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

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

    如何启用 Promise?

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

    如果我不想换包怎么办?

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

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

    【讨论】:

      【解决方案2】:

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

      如果不是这样,那么在交互过程中你需要调用

      1) 获取连接() 2) 执行查询 3) 调用 .release()

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

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

      【讨论】:

      • 或者只使用pool.query() 并让驱动程序处理获取连接并正确释放它。
      猜你喜欢
      • 2019-01-30
      • 2015-11-01
      • 2018-04-26
      • 1970-01-01
      • 1970-01-01
      • 2012-05-12
      • 1970-01-01
      • 2020-12-22
      • 1970-01-01
      相关资源
      最近更新 更多