Files
nxxmdata/backend/config/query-optimizer.js
2025-08-25 15:00:46 +08:00

246 lines
6.3 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.

/**
* 数据库查询优化器
* @file query-optimizer.js
* @description 监控和优化SQL查询性能
*/
const { sequelize } = require('./database-pool');
const { QueryTypes } = require('sequelize');
/**
* 记录查询性能
* @param {string} query SQL查询语句
* @param {number} executionTime 执行时间(毫秒)
* @returns {Promise<void>}
*/
async function logQueryPerformance(query, executionTime) {
try {
// 简化查询语句(移除参数值)
const simplifiedQuery = query.replace(/('([^']*)'|"([^"]*)"|`([^`]*)`)/g, '?');
// 记录到性能日志表
await sequelize.query(
'INSERT INTO query_performance_logs (query, execution_time, timestamp) VALUES (?, ?, NOW())',
{
replacements: [simplifiedQuery, executionTime],
type: QueryTypes.INSERT
}
).catch(() => {
// 如果表不存在,创建表
return sequelize.query(
`CREATE TABLE IF NOT EXISTS query_performance_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
query TEXT NOT NULL,
execution_time FLOAT NOT NULL,
timestamp DATETIME NOT NULL,
INDEX (timestamp),
INDEX (execution_time)
)`,
{ type: QueryTypes.RAW }
).then(() => {
// 重新尝试插入
return sequelize.query(
'INSERT INTO query_performance_logs (query, execution_time, timestamp) VALUES (?, ?, NOW())',
{
replacements: [simplifiedQuery, executionTime],
type: QueryTypes.INSERT
}
);
});
});
} catch (error) {
console.error('记录查询性能失败:', error);
}
}
/**
* 识别慢查询
* @param {number} threshold 慢查询阈值毫秒默认为500ms
* @returns {Promise<Array>} 慢查询列表
*/
async function identifySlowQueries(threshold = 500) {
try {
// 查询性能日志表中的慢查询
const slowQueries = await sequelize.query(
'SELECT query, AVG(execution_time) as avg_time, COUNT(*) as count, MAX(timestamp) as last_seen ' +
'FROM query_performance_logs ' +
'WHERE execution_time > ? ' +
'GROUP BY query ' +
'ORDER BY avg_time DESC',
{
replacements: [threshold],
type: QueryTypes.SELECT
}
).catch(() => {
// 如果表不存在,返回空数组
return [];
});
return slowQueries;
} catch (error) {
console.error('识别慢查询失败:', error);
return [];
}
}
/**
* 分析和优化表
* @param {string} tableName 表名
* @returns {Promise<Object>} 优化结果
*/
async function analyzeAndOptimizeTable(tableName) {
try {
// 分析表
await sequelize.query(`ANALYZE TABLE ${tableName}`, { type: QueryTypes.RAW });
// 优化表
const optimizeResult = await sequelize.query(`OPTIMIZE TABLE ${tableName}`, { type: QueryTypes.RAW });
return optimizeResult[0];
} catch (error) {
console.error(`分析和优化表 ${tableName} 失败:`, error);
return { error: error.message };
}
}
/**
* 获取表的索引信息
* @param {string} tableName 表名
* @returns {Promise<Array>} 索引信息
*/
async function getIndexInfo(tableName) {
try {
const indexInfo = await sequelize.query(
'SHOW INDEX FROM ??',
{
replacements: [tableName],
type: QueryTypes.SELECT
}
);
return indexInfo;
} catch (error) {
console.error(`获取表 ${tableName} 的索引信息失败:`, error);
return [];
}
}
/**
* 获取表信息
* @param {string} tableName 表名
* @returns {Promise<Object>} 表信息
*/
async function getTableInfo(tableName) {
try {
// 获取表状态
const tableStatus = await sequelize.query(
'SHOW TABLE STATUS LIKE ?',
{
replacements: [tableName],
type: QueryTypes.SELECT
}
);
// 获取表结构
const tableStructure = await sequelize.query(
'DESCRIBE ??',
{
replacements: [tableName],
type: QueryTypes.SELECT
}
);
return {
status: tableStatus[0] || {},
structure: tableStructure
};
} catch (error) {
console.error(`获取表 ${tableName} 信息失败:`, error);
return { error: error.message };
}
}
/**
* 解释查询计划
* @param {Object} query Sequelize查询对象
* @returns {Promise<Array>} 查询计划
*/
async function explainQuery(query) {
try {
// 获取SQL语句
const sql = query.getQueryString();
// 执行EXPLAIN
const explainResult = await sequelize.query(
`EXPLAIN ${sql}`,
{
type: QueryTypes.SELECT
}
);
return explainResult;
} catch (error) {
console.error('解释查询计划失败:', error);
return [];
}
}
/**
* 获取数据库状态
* @returns {Promise<Object>} 数据库状态
*/
async function getDatabaseStatus() {
try {
// 获取全局状态
const globalStatus = await sequelize.query(
'SHOW GLOBAL STATUS',
{ type: QueryTypes.SELECT }
);
// 转换为对象格式
const status = {};
globalStatus.forEach(item => {
if (item.Variable_name && item.Value) {
status[item.Variable_name] = item.Value;
}
});
// 提取关键指标
return {
connections: {
max_used: status.Max_used_connections,
current: status.Threads_connected,
running: status.Threads_running,
created: status.Threads_created,
cached: status.Threads_cached
},
queries: {
total: status.Questions,
slow: status.Slow_queries,
qps: status.Queries
},
buffer_pool: {
size: status.Innodb_buffer_pool_pages_total,
free: status.Innodb_buffer_pool_pages_free,
dirty: status.Innodb_buffer_pool_pages_dirty,
reads: status.Innodb_buffer_pool_reads,
read_requests: status.Innodb_buffer_pool_read_requests,
hit_rate: status.Innodb_buffer_pool_read_requests && status.Innodb_buffer_pool_reads
? (1 - parseInt(status.Innodb_buffer_pool_reads) / parseInt(status.Innodb_buffer_pool_read_requests)) * 100
: 0
}
};
} catch (error) {
console.error('获取数据库状态失败:', error);
return { error: error.message };
}
}
module.exports = {
logQueryPerformance,
identifySlowQueries,
analyzeAndOptimizeTable,
getIndexInfo,
getTableInfo,
explainQuery,
getDatabaseStatus
};