const { getDB, dbConfig } = require('./database'); const { logger } = require('./config/logger'); /** * 数据库连接监控工具 * 用于诊断和监控数据库连接池状态 */ class DatabaseMonitor { /** * 获取连接池详细状态 * @returns {Object} 连接池状态信息 */ /** * 获取连接池详细状态 * @returns {Object} 连接池状态信息 */ getPoolStatus() { try { const pool = getDB(); const status = { // 基本连接信息 totalConnections: pool._allConnections ? pool._allConnections.length : 0, freeConnections: pool._freeConnections ? pool._freeConnections.length : 0, acquiringConnections: pool._acquiringConnections ? pool._acquiringConnections.length : 0, // 计算使用率 connectionLimit: dbConfig.connectionLimit, usageRate: 0, // 配置信息 config: { connectionLimit: dbConfig.connectionLimit, acquireTimeout: dbConfig.acquireTimeout, timeout: dbConfig.timeout, idleTimeout: dbConfig.idleTimeout, maxLifetime: dbConfig.maxLifetime, queueLimit: dbConfig.queueLimit, host: dbConfig.host, database: dbConfig.database }, // 时间戳 timestamp: new Date().toISOString() }; // 计算连接使用率 if (status.connectionLimit > 0) { const usedConnections = status.totalConnections - status.freeConnections; status.usageRate = Math.round((usedConnections / status.connectionLimit) * 100); } return status; } catch (error) { logger.error('Failed to get pool status', { error: error.message }); return { error: error.message, timestamp: new Date().toISOString() }; } } /** * 测试数据库连接 * @returns {Object} 连接测试结果 */ async testConnection() { const startTime = Date.now(); let connection; try { const pool = getDB(); // 获取连接 const acquireStart = Date.now(); connection = await pool.getConnection(); const acquireTime = Date.now() - acquireStart; // 执行测试查询 const queryStart = Date.now(); const [result] = await connection.execute('SELECT 1 as test, NOW() as server_time'); const queryTime = Date.now() - queryStart; const totalTime = Date.now() - startTime; return { success: true, acquireTime, queryTime, totalTime, serverTime: result[0].server_time, connectionId: connection.threadId, timestamp: new Date().toISOString() }; } catch (error) { const totalTime = Date.now() - startTime; logger.error('Database connection test failed', { error: error.message, totalTime }); return { success: false, error: error.message, errorCode: error.code, totalTime, timestamp: new Date().toISOString() }; } finally { if (connection) { connection.release(); } } } /** * 执行连接池诊断 * @returns {Object} 诊断结果 */ async diagnose() { const poolStatus = this.getPoolStatus(); const connectionTest = await this.testConnection(); const diagnosis = { poolStatus, connectionTest, issues: [], recommendations: [], timestamp: new Date().toISOString() }; // 分析潜在问题 if (poolStatus.usageRate > 90) { diagnosis.issues.push('连接池使用率过高 (>90%)'); diagnosis.recommendations.push('考虑增加连接池大小或优化查询性能'); } if (poolStatus.freeConnections === 0) { diagnosis.issues.push('没有空闲连接可用'); diagnosis.recommendations.push('立即检查是否存在连接泄漏或增加连接池大小'); } if (!connectionTest.success) { diagnosis.issues.push(`数据库连接失败: ${connectionTest.error}`); diagnosis.recommendations.push('检查数据库服务器状态和网络连接'); } else { if (connectionTest.acquireTime > 5000) { diagnosis.issues.push('获取连接耗时过长 (>5秒)'); diagnosis.recommendations.push('检查连接池配置和数据库负载'); } if (connectionTest.queryTime > 1000) { diagnosis.issues.push('查询响应时间过长 (>1秒)'); diagnosis.recommendations.push('检查数据库性能和网络延迟'); } } return diagnosis; } /** * 生成监控报告 * @returns {string} 格式化的监控报告 */ async generateReport() { const diagnosis = await this.diagnose(); let report = '\n=== 数据库连接监控报告 ===\n'; report += `生成时间: ${diagnosis.timestamp}\n\n`; // 连接池状态 report += '【连接池状态】\n'; if (diagnosis.poolStatus.error) { report += `错误: ${diagnosis.poolStatus.error}\n`; } else { report += `总连接数: ${diagnosis.poolStatus.totalConnections}\n`; report += `空闲连接: ${diagnosis.poolStatus.freeConnections}\n`; report += `获取中连接: ${diagnosis.poolStatus.acquiringConnections}\n`; report += `连接限制: ${diagnosis.poolStatus.connectionLimit}\n`; report += `使用率: ${diagnosis.poolStatus.usageRate}%\n`; } // 连接测试 report += '\n【连接测试】\n'; if (diagnosis.connectionTest.success) { report += `状态: 成功\n`; report += `获取连接耗时: ${diagnosis.connectionTest.acquireTime}ms\n`; report += `查询耗时: ${diagnosis.connectionTest.queryTime}ms\n`; report += `总耗时: ${diagnosis.connectionTest.totalTime}ms\n`; report += `连接ID: ${diagnosis.connectionTest.connectionId}\n`; report += `服务器时间: ${diagnosis.connectionTest.serverTime}\n`; } else { report += `状态: 失败\n`; report += `错误: ${diagnosis.connectionTest.error}\n`; report += `错误代码: ${diagnosis.connectionTest.errorCode || 'N/A'}\n`; report += `总耗时: ${diagnosis.connectionTest.totalTime}ms\n`; } // 问题和建议 if (diagnosis.issues.length > 0) { report += '\n【发现的问题】\n'; diagnosis.issues.forEach((issue, index) => { report += `${index + 1}. ${issue}\n`; }); } if (diagnosis.recommendations.length > 0) { report += '\n【建议】\n'; diagnosis.recommendations.forEach((rec, index) => { report += `${index + 1}. ${rec}\n`; }); } if (diagnosis.issues.length === 0) { report += '\n【状态】\n数据库连接正常,未发现问题。\n'; } report += '\n=== 报告结束 ===\n'; return report; } /** * 启动实时监控 * @param {number} interval 监控间隔(毫秒),默认30秒 */ startMonitoring(interval = 30000) { console.log('启动数据库连接实时监控...'); const monitor = async () => { try { const status = this.getPoolStatus(); // 只在有问题时输出详细信息 if (status.usageRate > 80 || status.freeConnections < 2) { console.warn('数据库连接池警告:', { usageRate: `${status.usageRate}%`, freeConnections: status.freeConnections, totalConnections: status.totalConnections }); } // 减少频繁的日志记录,只在有问题时记录 if (status.usageRate > 80 || status.freeConnections < 2) { logger.warn('Database pool status warning', status); } // 注释掉正常情况下的日志记录 // logger.info('Database pool status', status); } catch (error) { console.error('监控过程中发生错误:', error); logger.error('Database monitoring error', { error: error.message }); } }; // 立即执行一次 monitor(); // 定期执行 const intervalId = setInterval(monitor, interval); // 返回停止函数 return () => { clearInterval(intervalId); console.log('数据库连接监控已停止'); }; } } // 创建单例实例 const dbMonitor = new DatabaseMonitor(); // 如果直接运行此文件,执行诊断 if (require.main === module) { (async () => { try { // 初始化数据库 const { initDB } = require('./database'); await initDB(); console.log('正在执行数据库连接诊断...'); const report = await dbMonitor.generateReport(); console.log(report); process.exit(0); } catch (error) { console.error('诊断失败:', error); process.exit(1); } })(); } module.exports = dbMonitor;