246 lines
6.3 KiB
JavaScript
246 lines
6.3 KiB
JavaScript
/**
|
||
* 数据库查询优化器
|
||
* @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
|
||
}; |