2025-09-04 10:49:10 +08:00
|
|
|
|
const mysql = require('mysql2/promise');
|
|
|
|
|
|
|
|
|
|
|
|
// 数据库配置
|
|
|
|
|
|
const dbConfig = {
|
2025-09-10 18:09:38 +08:00
|
|
|
|
// host: process.env.DB_HOST || '114.55.111.44',
|
|
|
|
|
|
// user: process.env.DB_USER || 'maov2',
|
|
|
|
|
|
// password: process.env.DB_PASSWORD || '5fYhw8z6T62b7heS',
|
|
|
|
|
|
// database: process.env.DB_NAME || 'maov2',
|
|
|
|
|
|
host: '114.55.111.44',
|
|
|
|
|
|
user: 'test_mao',
|
|
|
|
|
|
password: 'nK2mPbWriBp25BRd',
|
|
|
|
|
|
database: 'test_mao',
|
|
|
|
|
|
charset: 'utf8mb4',
|
|
|
|
|
|
dateStrings: true,
|
|
|
|
|
|
// 连接池配置
|
|
|
|
|
|
connectionLimit: 20, // 连接池最大连接数
|
2025-09-15 17:28:12 +08:00
|
|
|
|
// queueLimit: 10, // 排队等待连接的最大数量,0表示无限制
|
2025-09-10 18:09:38 +08:00
|
|
|
|
// 连接超时配置
|
|
|
|
|
|
// acquireTimeout: 60000, // 获取连接超时时间 60秒
|
|
|
|
|
|
// timeout: 60000, // 查询超时时间 60秒
|
|
|
|
|
|
// reconnect: true, // 自动重连
|
|
|
|
|
|
// 连接保活配置
|
2025-09-15 17:28:12 +08:00
|
|
|
|
// multipleStatements: true,
|
2025-09-10 18:09:38 +08:00
|
|
|
|
// 空闲连接超时配置
|
2025-09-15 17:28:12 +08:00
|
|
|
|
// idleTimeout: 300000, // 5分钟空闲超时
|
2025-09-10 18:09:38 +08:00
|
|
|
|
// maxLifetime: 1800000, // 30分钟最大生命周期
|
|
|
|
|
|
// 连接保活设置
|
2025-09-15 17:28:12 +08:00
|
|
|
|
// keepAliveInitialDelay: 0, // 开始保活探测前的延迟时间
|
|
|
|
|
|
// enableKeepAlive: true, // 启用TCP保活
|
2025-09-10 18:09:38 +08:00
|
|
|
|
// 添加类型转换配置
|
|
|
|
|
|
typeCast: function (field, next) {
|
|
|
|
|
|
if (field.type === 'TINY' && field.length === 1) {
|
|
|
|
|
|
return (field.string() === '1'); // 1 = true, 0 = false
|
|
|
|
|
|
}
|
|
|
|
|
|
return next();
|
|
|
|
|
|
},
|
|
|
|
|
|
// 确保参数正确处理
|
|
|
|
|
|
supportBigNumbers: true,
|
|
|
|
|
|
bigNumberStrings: false
|
2025-09-04 10:49:10 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 创建数据库连接池
|
|
|
|
|
|
let pool;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 初始化数据库连接池
|
|
|
|
|
|
* @returns {Promise<mysql.Pool>} 数据库连接池
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function initDB() {
|
2025-09-10 18:09:38 +08:00
|
|
|
|
if (!pool) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
pool = mysql.createPool(dbConfig);
|
2025-09-04 10:49:10 +08:00
|
|
|
|
|
2025-09-10 18:09:38 +08:00
|
|
|
|
// 添加连接池事件监听
|
|
|
|
|
|
pool.on('connection', function (connection) {
|
|
|
|
|
|
console.log('新的数据库连接建立:', connection.threadId);
|
|
|
|
|
|
});
|
2025-09-04 10:49:10 +08:00
|
|
|
|
|
2025-09-10 18:09:38 +08:00
|
|
|
|
// 注释掉频繁的连接获取和释放日志,避免日志过多
|
|
|
|
|
|
// pool.on('acquire', function (connection) {
|
|
|
|
|
|
// console.log('连接池获取连接:', connection.threadId);
|
|
|
|
|
|
// });
|
2025-09-04 10:49:10 +08:00
|
|
|
|
|
2025-09-10 18:09:38 +08:00
|
|
|
|
// pool.on('release', function (connection) {
|
|
|
|
|
|
// console.log('连接池释放连接:', connection.threadId);
|
|
|
|
|
|
// });
|
2025-09-04 10:49:10 +08:00
|
|
|
|
|
2025-09-10 18:09:38 +08:00
|
|
|
|
pool.on('error', function (err) {
|
|
|
|
|
|
console.error('数据库连接池错误:', err);
|
|
|
|
|
|
if (err.code === 'PROTOCOL_CONNECTION_LOST') {
|
|
|
|
|
|
console.log('数据库连接丢失,尝试重新连接...');
|
|
|
|
|
|
} else if (err.code === 'ECONNRESET') {
|
|
|
|
|
|
console.log('数据库连接被重置,尝试重新连接...');
|
|
|
|
|
|
} else if (err.code === 'ETIMEDOUT') {
|
|
|
|
|
|
console.log('数据库连接超时,尝试重新连接...');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-09-04 10:49:10 +08:00
|
|
|
|
|
2025-09-10 18:09:38 +08:00
|
|
|
|
// 测试连接
|
|
|
|
|
|
const connection = await pool.getConnection();
|
|
|
|
|
|
console.log('数据库连接池初始化成功');
|
|
|
|
|
|
connection.release();
|
2025-09-04 10:49:10 +08:00
|
|
|
|
|
2025-09-10 18:09:38 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('数据库连接池初始化失败:', error);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
2025-09-04 10:49:10 +08:00
|
|
|
|
}
|
2025-09-10 18:09:38 +08:00
|
|
|
|
return pool;
|
2025-09-04 10:49:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取数据库连接池
|
|
|
|
|
|
* @returns {mysql.Pool} 数据库连接池
|
|
|
|
|
|
*/
|
|
|
|
|
|
function getDB() {
|
2025-09-10 18:09:38 +08:00
|
|
|
|
if (!pool) {
|
|
|
|
|
|
throw new Error('数据库连接池未初始化,请先调用 initDB()');
|
|
|
|
|
|
}
|
|
|
|
|
|
return pool;
|
2025-09-04 10:49:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 执行数据库查询(带重试机制)
|
|
|
|
|
|
* @param {string} sql SQL查询语句
|
|
|
|
|
|
* @param {Array} params 查询参数
|
|
|
|
|
|
* @param {number} retries 重试次数
|
|
|
|
|
|
* @returns {Promise<Array>} 查询结果
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function executeQuery(sql, params = [], retries = 3) {
|
2025-09-10 18:09:38 +08:00
|
|
|
|
for (let i = 0; i < retries; i++) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const connection = await pool.getConnection();
|
|
|
|
|
|
try {
|
|
|
|
|
|
const [results] = await connection.execute(sql, params);
|
|
|
|
|
|
connection.release();
|
|
|
|
|
|
return results;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
connection.release();
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(`数据库查询失败 (尝试 ${i + 1}/${retries}):`, error.message);
|
|
|
|
|
|
|
|
|
|
|
|
if (i === retries - 1) {
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果是连接相关错误,等待后重试
|
|
|
|
|
|
if (error.code === 'PROTOCOL_CONNECTION_LOST' ||
|
|
|
|
|
|
error.code === 'ECONNRESET' ||
|
|
|
|
|
|
error.code === 'ETIMEDOUT') {
|
|
|
|
|
|
console.log(`等待 ${(i + 1) * 1000}ms 后重试...`);
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, (i + 1) * 1000));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-04 10:49:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 关闭数据库连接池
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function closeDB() {
|
2025-09-10 18:09:38 +08:00
|
|
|
|
if (pool) {
|
|
|
|
|
|
await pool.end();
|
|
|
|
|
|
pool = null;
|
|
|
|
|
|
console.log('数据库连接池已关闭');
|
|
|
|
|
|
}
|
2025-09-04 10:49:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = {
|
2025-09-10 18:09:38 +08:00
|
|
|
|
initDB,
|
|
|
|
|
|
getDB,
|
|
|
|
|
|
closeDB,
|
|
|
|
|
|
executeQuery,
|
|
|
|
|
|
dbConfig
|
2025-09-04 10:49:10 +08:00
|
|
|
|
};
|