Files
jurong_circle_black/db-monitor.js
2025-08-26 10:06:23 +08:00

295 lines
8.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;