NodeJS使用cluster,进程间如何共享一个大的数组(比如4GB的数据)

NodeJS使用cluster,进程间如何共享一个大的数组(比如4GB的数据)

不要读文件,那样IO压力太大了……

23 回复

NodeJS 使用 cluster 进程间如何共享一个大的数组(比如4GB的数据)

背景

在使用 Node.js 的 cluster 模块时,我们经常需要在不同的工作进程中共享数据。然而,直接在主进程中创建一个大的数组并试图让所有工作进程访问它并不是一个可行的方案。由于每个工作进程都是独立的 V8 实例,它们之间无法直接共享内存。

解决方案

为了解决这个问题,我们可以使用 worker_threads 模块来实现进程间的通信 (IPC)。worker_threads 模块允许我们在不同的线程或进程中共享数据。对于非常大的数组,可以考虑使用 SharedArrayBuffer 或者通过序列化(如 JSON.stringify 和 JSON.parse)来传输数据。

示例代码

// 主进程代码
const { Worker, isMainThread, parentPort } = require('worker_threads');

if (isMainThread) {
    const worker = new Worker(__filename);
    const largeArray = new Uint32Array(1000000); // 创建一个大的数组

    // 填充数组数据
    for (let i = 0; i < largeArray.length; i++) {
        largeArray[i] = i;
    }

    // 将数据发送给子进程
    worker.postMessage({ type: 'DATA', data: largeArray.buffer });
} else {
    // 子进程代码
    parentPort.on('message', (msg) => {
        if (msg.type === 'DATA') {
            const receivedArray = new Uint32Array(msg.data);
            console.log(receivedArray[0], receivedArray[999999]); // 输出第一个和最后一个元素
        }
    });
}

解释

  1. 主进程

    • 创建一个大的数组 largeArray
    • 通过 Worker 创建一个新的工作线程,并将数组的 buffer 发送到工作线程。
  2. 子进程

    • 监听来自主进程的消息。
    • 接收到消息后,根据接收到的 buffer 创建一个新的 Uint32Array 对象,并打印出第一个和最后一个元素以验证数据是否正确传输。

这种方法避免了 IO 压力,因为数据没有被写入或读取文件系统。通过 worker_threads 模块,我们可以高效地在不同进程间共享数据。


这么大的数组就应该放数据库里

v8已经限制了nodejs每个进程最大内存上限最多就是1个多G,如果你的需求就是4个G的话,那就别想了

你只有把这块数据的读写封装成外部服务,通过tcp或socket来读写,比如redis实际上就可以看作进程间共享内存数据的一个形式

4G内存的数据可以在Node.js实现的,buffer的内存管理不受V8限制的

还有一种糙快猛的搞法,把4G内存虚拟成硬盘,把文件放到虚拟盘里,用标准的fs api访问 这个办法是我乱想的,后果自负哈

需要持久 === mongodb 不用持久 === redis 4G都放在nodejs,压力山大

一次从数据库读4G的数据,你确定你的应用流程或者算法没问题?是不是可以再优化一下?

4G数据就大了啊?小伙伴们,木有解决方案么?

smalloc我没看到进程间如何共享使用啊! 比如有没有这样的库

var shm = require(“shm”);//假设有这么一个库 if(cluster.isMaster) { //在这里创建bigData shm.create(“bigData”, [……]);//至少4GB的数据 for(var i=0;i<=10;i++) { cluster.fork(); } } else { //在这里使用bigData var bigData = shm.get(“bigData”);//bigData指向create中创建的那个数组 //在这里使用…… }

搞一个独立的node进程,里边只放这个数组,开socket服务,提供操作数组的接口 其他进程通过socket操作数组

这样还不如用把数组分拆,用redis存(redis键值有大小限制),我觉得这样才是最好的,用持久化数据库要读硬盘太慢了

用内存数据库保存就行了,像Redis, MongoDB内存表,MySQL内存表

我知道有内存数据库,但是我想在NodeJS中原生实现集群对大数据内存的访问

晕了,现在也遇到类似的问题,求解决方案。

另外请教楼主,如果是key-value缓存,用一个大的对象来进行,性能上会不会有什么问题?

例如这样的对象: cache_obj = { key1: value1, key2:value2, ... 上百万这样的键值对... }

然后需要的时候直接读取: cache_obj[‘key1’],有没有更好的缓存数据供高性能访问?

自己实现吧,用node addon封装一下boost的Shared Memory。但是即便如此,想直接将共享内存作为数组来用,也不会太方便。毕竟V8的数组是被V8自己的垃圾回收所管理的。

一定要数据达到4G才能共享吗?这样做是不是有些死磕的感觉。:)

哎哟,自己封装shm算了,写好了开源吧

我就想问下 没有楼主说的那么大的数组 进程间该如何共享?

cluster.send 的数据,有大小限制吗?

需要支持跨服务器间调用么

在 Node.js 中使用 cluster 模块时,如果需要在多个工作进程中共享一个大数组(如4GB的数据),直接在内存中共享这样的数据是不现实的,因为每个工作进程都有自己独立的 V8 引擎实例。不过,你可以通过一些间接的方法来实现类似的功能。

使用共享内存 (Shared Memory)

Node.js 本身并没有提供直接的共享内存支持,但可以借助一些第三方库来实现。例如,node-sharedmem 这个库可以用来创建共享内存段。

示例代码

const cluster = require('cluster');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    console.log(`Master process is running as ${process.pid}`);

    // 创建一个共享内存
    const SharedArrayBuffer = require('buffer').SlowBuffer;
    let sharedBuffer = new SharedArrayBuffer(4 * 1024 * 1024 * 1024); // 4GB

    // 创建子进程
    for (let i = 0; i < numCPUs - 1; i++) {
        cluster.fork();
    }

    // 向子进程传递共享内存对象
    cluster.on('fork', (worker) => {
        worker.send({ type: 'init', buffer: sharedBuffer });
    });

} else {
    process.on('message', (msg) => {
        if (msg.type === 'init') {
            console.log(`Worker ${process.pid} received the shared memory buffer`);
            const data = new Uint8Array(msg.buffer);

            // 在这里你可以对共享内存进行操作
            for (let i = 0; i < data.length; i++) {
                data[i] = i % 256; // 初始化数据
            }
        }
    });
}

解释

  • 主进程:创建一个4GB大小的共享内存,并将该内存传递给每一个工作进程。
  • 工作进程:接收到共享内存后,可以在其中写入或读取数据。

注意事项

  • 共享内存的操作必须保证线程安全,以避免竞争条件。
  • 这种方法依赖于底层的内存共享机制,可能会受到系统限制,如地址空间的大小限制等。

这种方法虽然可以实现进程间的共享内存访问,但性能开销和复杂性都会增加。在实际应用中,还需要根据具体需求进行优化。

回到顶部