Node.js集群环境下数据库单例模式失效问题排查
问题原因
你的单例模式在单个Node.js进程内是有效的,但因为使用了cluster模块,而cluster的每个worker都是独立的V8进程,内存完全隔离——每个worker都会重新加载你的database模块,各自初始化自己的database.instance,自然会创建多份数据库连接。
解决方案
根据业务场景,有两种常用处理方式:
方式一:主进程统一管理数据库连接,Worker通过IPC通信
让主进程单独初始化数据库连接,所有worker不直接连接数据库,而是通过进程间通信(IPC)把数据库操作请求发给主进程,主进程处理后返回结果。这样所有worker共用一套连接池,不会重复创建实例。
示例代码参考:
主进程代码
const cluster = require('cluster'); const db = require('./database'); // 主进程初始化数据库 if (cluster.isPrimary) { // 监听worker的数据库操作请求 cluster.on('message', (worker, msg) => { if (msg.type === 'mysql_query') { db.mysql.query(msg.sql, msg.params) .then(result => worker.send({ type: 'mysql_result', id: msg.id, result })) .catch(err => worker.send({ type: 'mysql_error', id: msg.id, err })); } }); // 启动worker进程 const cpuCount = require('os').cpus().length; for (let i = 0; i < cpuCount; i++) { cluster.fork(); } } else { // Worker进程处理业务请求,通过IPC委托主进程操作数据库 app.get('/get-data', (req, res) => { const queryId = Date.now(); // 用ID标识请求,避免结果混淆 process.send({ type: 'mysql_query', id: queryId, sql: 'SELECT * FROM your_table WHERE id = ?', params: [req.query.id] }); // 监听主进程返回的结果 const handleResult = (msg) => { if (msg.id === queryId) { process.off('message', handleResult); if (msg.type === 'mysql_result') { res.json(msg.result); } else { res.status(500).send(msg.err.message); } } }; process.on('message', handleResult); }); }
方式二:每个Worker维持独立连接池,但严格限制连接数
如果业务需要worker直接操作数据库(避免IPC的性能开销),那每个worker都会创建自己的连接池,此时必须降低单个worker的连接池上限,防止数据库连接数被打满。
原代码里connectionLimit: 10000的设置完全不合理,数据库默认最大连接数通常在100-1000区间。假设你有8个worker,可将单个worker的连接数设为10,总连接数控制在80以内:
createMysql() { this.mysql = mysqlModule.createPool({ // ...其他原有配置 connectionLimit: 10, // 根据worker数量和数据库最大连接数灵活调整 // ...其他原有配置 }); }
这种方式下每个worker仍会创建独立实例,但连接数处于可控范围,不会造成资源浪费或数据库连接溢出。
补充说明
你之前尝试把instance变量放到类外部无效,本质原因是cluster的每个worker拥有独立内存空间,模块内的变量在每个worker进程中都会重新初始化,跨进程无法共享任何内存数据。
内容的提问来源于stack exchange,提问作者Cops Kill
相关产品推荐
相关产品推荐

