Node.js使用MySQL连接池每日请求超时,服务停滞需重启求助
我在Node.js服务器中使用MySQL连接池处理数据库连接,该服务器为Web应用提供服务,部署在Heroku平台,MySQL数据库托管于Google SQL。每日会出现数次服务完全停滞的情况,必须重启服务器才能恢复,经排查发现问题在于某一时刻所有数据库请求均会超时。
连接池初始化代码
global.pool; function initPool() // called on Server starts { const mysql = require("mysql"); // mysql@2.18.1 pool = mysql.createPool({ connectionLimit : 30, host : '******', port : '******', user : '******', password : '******', database : '******', waitForConnections : true // If true, the pool will queue the connection request and call it when one becomes available. }); pool.on('acquire', function (connection) { console.log('[mysql] Connection %d acquired', connection.threadId); }); pool.on('connection', function (connection) { console.log('[mysql] Connection %d connected', connection.threadId); }); pool.on('enqueue', function () { console.log('[mysql] enqueue. waiting for available connection slot'); }); pool.on('release', function (connection) { console.log('[mysql] Connection %d released', connection.threadId); }); pool.on('error', function (err) { console.error(err); }); }
查询执行代码
static getSomething(condition) { var deferred = Q.defer(); var query = ` SELECT * FROM table WHERE field = "${condition}"; `; pool.query(query, function(error, results, fields) { if(error) { deferred.reject(error); return; } deferred.resolve(results); }); return deferred.promise; }
请问我是否忽略了某些配置问题?为何每日都需要重启服务器?是否需要采用不同的方式管理数据库连接的开闭?
1. 缺失连接有效性检测配置
Google Cloud SQL与Heroku之间的跨云网络容易出现闲置连接被防火墙断开的情况,当前连接池未配置连接存活检测与超时参数,导致失效连接长期占用池资源,最终耗尽所有可用连接。
需补充以下关键配置:
pool = mysql.createPool({ connectionLimit: 30, host: '******', port: '******', user: '******', password: '******', database: '******', waitForConnections: true, connectTimeout: 10000, // 10秒内无法建立连接则超时 acquireTimeout: 10000, // 10秒内无法获取池连接则超时 waitTimeout: 60000, // 连接闲置60秒后自动释放 keepAlive: true, // 启用连接心跳 keepAliveInitialDelay: 30000 // 连接建立30秒后开始发送心跳包 });
2. 错误处理不完善导致连接泄漏
当前pool.on('error')仅打印错误,未处理失效连接;单个连接出错时也未主动销毁,导致无效连接留在池中,后续请求反复尝试使用这些连接,最终占满连接池。
优化错误处理逻辑:
// 池级错误处理 pool.on('error', function (err) { console.error('[mysql] Pool error:', err); // 连接丢失时重建池(或让池自动处理) if (err.code === 'PROTOCOL_CONNECTION_LOST') { initPool(); } else { throw err; } }); // 单个连接的错误处理 pool.on('connection', function (connection) { console.log('[mysql] Connection %d connected', connection.threadId); connection.on('error', function(err) { console.error('[mysql] Connection %d error:', connection.threadId, err); // 主动销毁出错的连接,避免占用池资源 connection.destroy(); }); });
3. 字符串拼接SQL引发的风险
当前查询用字符串拼接生成SQL,存在严重SQL注入风险,同时若condition包含特殊字符,可能导致查询执行阻塞,占用连接不释放。必须改用参数化查询:
static getSomething(condition) { var deferred = Q.defer(); // 使用?作为占位符,由驱动自动处理参数转义 const query = 'SELECT * FROM table WHERE field = ?'; pool.query(query, [condition], function(error, results, fields) { if(error) { deferred.reject(error); return; } deferred.resolve(results); }); return deferred.promise; }
4. 无队列长度限制引发服务雪崩
waitForConnections: true会让所有请求进入队列等待连接,若队列无限增长,会耗尽内存导致服务停滞。需设置queueLimit限制队列长度,超出时直接返回错误:
pool = mysql.createPool({ // ... 其他配置 queueLimit: 100 // 最多允许100个请求排队等待 });
5. 连接数配置可能超限
Google Cloud SQL实例有连接数配额限制(基础实例通常为25-100),当前connectionLimit:30可能接近或超出配额,需查看实例配额后调整该值,避免被数据库端拒绝连接。
核心原因总结
每日服务停滞的根本原因是失效连接未被及时回收,导致连接池耗尽,所有请求排队等待超时。通过上述配置优化与代码调整,即可解决问题,无需频繁重启服务器。
内容的提问来源于stack exchange,提问作者DeLac

