quicklite
Version:
A lightweight ORM toolkit for SQLite in Node.js applications
202 lines (153 loc) • 5.88 kB
Markdown
# 查询分析器 (QueryAnalyzer)
`QueryAnalyzer` 是一个用于分析 SQLite 查询性能并提供优化建议的工具类。它可以帮助开发者识别低效查询、检测全表扫描、分析索引使用情况,并为复杂查询提供索引建议。
> 兼容 better-sqlite3 v11.8.1 和 SQLite 3.48.0
## 功能特性
1. **查询执行计划分析**:解析 SQLite EXPLAIN QUERY PLAN 输出,识别可能的性能问题
2. **索引使用检测**:检查查询是否正在使用可用索引,识别索引扫描和表扫描
3. **性能建议生成**:根据查询结构和表结构生成优化建议
4. **执行时间测量**:测量查询执行时间,帮助识别慢查询
5. **索引建议**:基于查询模式自动建议创建索引
## 基本用法
### 分析 SQL 查询
```typescript
import { QueryAnalyzer } from 'quicklite';
import Database from 'better-sqlite3';
const db = new Database('myapp.db');
// 分析简单查询
const analysis = QueryAnalyzer.analyze(
db,
'SELECT * FROM users WHERE age > 30'
);
console.log('查询:', analysis.sql);
console.log('执行时间:', analysis.executionTime, 'ms');
console.log('查询计划:');
analysis.queryPlan.forEach(p => {
console.log(`- ${p.detail}`);
});
console.log('性能建议:');
analysis.suggestions.forEach(s => {
console.log(`- ${s}`);
});
// 分析带参数的查询
const paramQueryAnalysis = QueryAnalyzer.analyze(
db,
'SELECT * FROM users WHERE email = ?',
['user@example.com']
);
```
### 获取索引建议
```typescript
// 获取复杂查询的索引建议
const indexSuggestions = QueryAnalyzer.suggestIndices(
db,
`SELECT u.name, o.product, SUM(o.amount) as total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.age > 30
GROUP BY u.id, o.product
ORDER BY total DESC`
);
console.log('建议为以下列创建索引:');
indexSuggestions.forEach(s => {
console.log(`- ${s}`);
});
```
## API 参考
### QueryAnalyzer 类
#### 静态方法
- **analyze(db: Database, sql: string, params?: any[]): QueryAnalysisResult**
分析 SQL 查询并返回详细的分析结果。
参数:
- `db`: Better-SQLite3 数据库实例
- `sql`: 要分析的 SQL 查询语句
- `params` (可选): 查询的参数值
返回: 包含查询计划、执行时间和优化建议的 `QueryAnalysisResult` 对象
- **analyzePlan(queryPlan: QueryPlan[], sql: string): string[]**
根据查询计划生成优化建议。
参数:
- `queryPlan`: 查询计划数组
- `sql`: SQL 查询语句
返回: 优化建议字符串数组
- **extractTableNames(sql: string): string[]**
从 SQL 查询中提取表名。
参数:
- `sql`: SQL 查询语句
返回: 表名字符串数组
- **suggestIndices(db: Database, sql: string): string[]**
为给定的 SQL 查询建议可能的索引。
参数:
- `db`: Better-SQLite3 数据库实例
- `sql`: SQL 查询语句
返回: 索引建议字符串数组
### 类型定义
```typescript
// 查询计划项
interface QueryPlan {
id: number;
parentId: number | null;
detail: string;
}
// 查询分析结果
interface QueryAnalysisResult {
sql: string;
queryPlan: QueryPlan[];
executionTime: number;
suggestions: string[];
}
```
## 性能建议类型
QueryAnalyzer 可以提供多种类型的性能建议,包括但不限于:
1. **缺少索引建议**:当查询包含 WHERE、JOIN 或 ORDER BY 子句但没有相应索引时
2. **全表扫描警告**:当查询需要扫描整个表而不是使用索引时
3. **临时表使用提醒**:当查询需要创建临时表进行排序或分组时
4. **复合索引建议**:当查询中的多个条件可以通过单个复合索引优化时
5. **查询重构建议**:推荐可能更高效的查询写法
## 最佳实践
1. **定期分析关键查询**:使用 QueryAnalyzer 定期检查应用中最常用和最复杂的查询
2. **验证索引效果**:添加建议的索引后,再次使用 QueryAnalyzer 分析以确认性能改进
3. **在开发阶段使用**:尽早发现并解决性能问题,而不是等到生产环境
4. **权衡索引数量**:添加索引会提高查询速度,但也会增加写入开销和存储空间,需要合理平衡
## 示例应用场景
### 优化数据报表查询
```typescript
// 分析复杂的报表查询
const reportQueryAnalysis = QueryAnalyzer.analyze(
db,
`SELECT
u.department,
COUNT(DISTINCT u.id) as userCount,
SUM(o.amount) as totalAmount
FROM users u
LEFT JOIN orders o ON u.id = o.userId
WHERE u.joinDate > ?
GROUP BY u.department
ORDER BY totalAmount DESC`,
[Date.now() - 90 * 24 * 60 * 60 * 1000] // 过去90天
);
// 应用建议的索引
reportQueryAnalysis.suggestions
.filter(s => s.includes('考虑为以下列创建索引'))
.forEach(suggestion => {
const match = suggestion.match(/为\s+(\w+)\.(\w+)\s+创建索引/);
if (match) {
const [_, table, column] = match;
const indexName = `idx_${table}_${column}`;
db.exec(`CREATE INDEX IF NOT EXISTS ${indexName} ON ${table}(${column})`);
console.log(`已创建索引: ${indexName}`);
}
});
```
### 监控应用查询性能
```typescript
// 在应用中集成查询监控
function monitorQuery(db, sql, params = []) {
const analysis = QueryAnalyzer.analyze(db, sql, params);
// 记录耗时超过100ms的查询
if (analysis.executionTime > 100) {
logger.warn(`慢查询检测: ${sql}`);
logger.warn(`执行时间: ${analysis.executionTime}ms`);
logger.warn(`优化建议: ${analysis.suggestions.join('\n')}`);
}
return db.prepare(sql).all(params);
}
```