Files
jurong_circle_black/db-monitor.js

295 lines
8.8 KiB
JavaScript
Raw Normal View History

2025-08-26 10:06:23 +08:00
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;