首页 > 解决方案 > 是否可以使用 Node 工作线程来执行数据库插入?

问题描述

我最近阅读了 Node 的“worker_threads”模块,该模块允许在多个线程中并行执行 Javascript 代码,这对于 CPU 密集型操作很有用。(注意:这些不是 Chrome 在浏览器中制作的网络工作者)

我正在构建一个功能,我需要在不阻塞浏览器的情况下执行大量 Postgres INSERT。

问题是:在我实例化 worker 的 Javascript 文件中,我不允许导入任何内容,包括本地 Node 模块或 NPM 库(如 Knex.js),这是执行数据库查询所必需的。我收到一条错误消息:一旦执行文件,就无法在模块外部使用 import 语句。

我尝试将工作代码放在另一个文件中,并在顶部使用 import 语句(同样的错误)。我尝试将 Knex 对象提供给 workerData,但它无法克隆非本地 JS 对象。

我没有想法 - 如果我们无法导入任何 NPM 库,有谁知道如何在工作线程中与数据库交互?!?!

// mainThread.js

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

import knex from 'knex'; // --> *** UNCAUGHT EXCEPTION: Cannot use import statement outside a module ***

if (isMainThread) {
  module.exports = async function runWorker (rowsToInsert = []) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, { workerData: { rowsToInsert } });

      worker.on('message', (returningRows) => resolve(returningRows));
      worker.on('error', reject);
      worker.on('exit', (code) => {
        if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`));
      });
    });
  };
} else {
  const { rowsToInsert } = workerData;

  return knex('table').insert(rowsToInsert)
    .then((returningRows) => {
      parentPort.postMessage({ data: returningRows });
    });
}

我正在关注此网页上的教程:https ://blog.logrocket.com/use-cases-for-node-workers/

标签: node.jsnode-clusterworker-threadimport-modulenode-worker-threads

解决方案


这当然是可能的,但这是一个非常糟糕的主意。

数据库驱动程序已经是异步且非阻塞的 JavaScript 线程。按照您的建议将插入调用移动到单独的线程不仅不会获得性能提升,而且实际上会降低整体性能,因为线程间通信涉及的开销:

  • 同步和消息传递不是免费的
  • JavaScript在线程之间移动数据时使用结构化克隆。这意味着您rowsToInsert必须复制所有内容,这(相对)昂贵。

通常,真正适合使用 JS 线程的唯一时间是当您的 JavaScript 代码执行 CPU 密集型工作时。节点文档在顶部说了这么多

工作者(线程)对于执行 CPU 密集型 JavaScript 操作很有用。它们对 I/O 密集型工作没有多大帮助。Node.js 内置的异步 I/O 操作比 Workers 效率更高。

这意味着如果您正在执行大量解析、数学或类似操作,则可能适合在线程中完成工作。然而,简单地将数据从一个地方铲到另一个地方(即 I/O)并不是线程的理想选择——毕竟,节点的设计在这种工作中被调整为高效。

你没有说你rowsToInsert来自哪里,但如果它来自 HTTP 请求,那么使用线程是错误的。但是,如果您在服务器上解析例如 CSV 或 JSON 文件,那么在线程中执行此操作可能是值得的,但线程完成所有工作很重要(因此内存不需要在线程之间移动)。您发布给工作人员的消息应该只是“处理位于 /foo/bar.csv 的文件”,然后工作线程完成其余的工作。


您遇到的错误与没有工作线程时遇到的错误相同:您尝试import在常规的非模块 JS 文件中使用。将工作文件重命名为 *.mjs 或require('knex')改用。

node 的ES 模块文档详细介绍了importrequire.


推荐阅读