在Node.js项目里,使用MySQL连接池管理数据库连接,再配合事务处理多步关联的数据库操作,是很多业务场景的标配方案。合理的连接池配置和事务管理方式,直接影响着数据库操作的性能和系统的稳定性。

MySQL连接池的核心配置
使用mysql2库创建连接池时,需要根据业务并发量调整核心参数,避免连接数不足或者连接浪费。以下是常用的配置项说明:
| 配置项 | 作用 | 建议值 |
|---|---|---|
| connectionLimit | 连接池最大连接数 | 根据服务器CPU核数和数据库最大连接数调整,一般设置为20-50 |
| waitForConnections | 连接不足时是否等待 | 设置为true,避免直接报错 |
| queueLimit | 等待队列最大长度,0为无限制 | 设置为0,避免请求被丢弃 |
| idleTimeout | 空闲连接超时时间,单位毫秒 | 设置为60000,及时回收无用连接 |
创建连接池的示例代码如下:
const mysql = require('mysql2/promise');
// 创建连接池
const pool = mysql.createPool({
host: '127.0.0.1',
user: 'root',
password: '123456',
database: 'test_db',
connectionLimit: 30,
waitForConnections: true,
queueLimit: 0,
idleTimeout: 60000
});
事务的正确使用流程
事务需要保证ACID特性,在连接池中使用事务时,必须从连接池获取专属连接,所有事务操作都使用这个连接,事务结束后要释放连接回池。标准流程如下:
- 从连接池获取一个连接
- 开启事务
- 执行多个数据库操作
- 操作成功则提交事务,失败则回滚事务
- 释放连接回连接池
完整的事务操作代码示例:
async function transferMoney(fromId, toId, amount) {
let connection;
try {
// 从连接池获取连接
connection = await pool.getConnection();
// 开启事务
await connection.beginTransaction();
// 执行转出操作
const [outResult] = await connection.execute(
'UPDATE account SET balance = balance - ? WHERE id = ?',
[amount, fromId]
);
if (outResult.affectedRows === 0) {
throw new Error('转出账户不存在或余额不足');
}
// 执行转入操作
const [inResult] = await connection.execute(
'UPDATE account SET balance = balance + ? WHERE id = ?',
[amount, toId]
);
if (inResult.affectedRows === 0) {
throw new Error('转入账户不存在');
}
// 提交事务
await connection.commit();
console.log('转账成功');
} catch (err) {
// 出错回滚事务
if (connection) {
await connection.rollback();
}
console.error('转账失败:', err.message);
} finally {
// 释放连接回池
if (connection) {
connection.release();
}
}
}
性能优化与管理技巧
避免连接泄漏
连接泄漏是使用连接池最常见的问题,表现为连接数持续上涨,最终耗尽连接池。必须保证所有获取的连接都在finally块中释放,即使是事务出错也不能遗漏。可以在连接池配置中开启连接泄漏检测:
const pool = mysql.createPool({
// 其他配置项
debug: false,
// 开启连接泄漏检测,超过指定时间未释放的连接会打印警告
leakDetectionTimeout: 30000
});
控制事务粒度
事务的粒度越大,持有连接的时间越长,会影响连接池的并发能力。尽量把非数据库操作(比如接口调用、文件读写)放在事务外面,只把必须的数据库操作放在事务内部,减少事务执行时间。
合理设置事务超时时间
如果事务执行时间过长,会阻塞其他操作,甚至触发数据库的事务超时机制。可以在开启事务后设置当前连接的超时时间,避免长时间占用连接:
async function runLongTransaction() {
let connection;
try {
connection = await pool.getConnection();
await connection.beginTransaction();
// 设置当前事务超时时间为10秒
await connection.execute('SET SESSION innodb_lock_wait_timeout = 10');
// 执行事务操作
await connection.commit();
} catch (err) {
if (connection) await connection.rollback();
console.error(err);
} finally {
if (connection) connection.release();
}
}
复用连接池实例
不要在每次请求时都创建新的连接池,应该在项目启动时创建一个全局的连接池实例,所有数据库操作都复用这个实例,避免重复创建连接池带来的性能损耗。
常见问题排查
如果遇到事务执行慢或者连接池报错,可以按照以下步骤排查:
- 检查连接池的connectionLimit是否过小,无法满足并发需求
- 查看是否有未释放的连接,通过leakDetectionTimeout定位泄漏点
- 检查事务中的SQL语句是否有慢查询,添加合适的索引
- 确认事务粒度是否过大,是否包含了不必要的操作
注意:事务中的操作尽量使用参数化查询,避免SQL注入风险,同时参数化查询也能让数据库更好地复用执行计划,提升性能。