在前五篇中,我们完成了JDBC驱动、存储引擎和事务管理器的实现,支持了SQL执行、数据存储和事务一致性。本篇将聚焦查询优化器(query-optimizer
模块)的实现,设计基于成本的优化策略,优化SQL查询的执行计划,确保高性能和资源效率。查询优化器将处理复杂查询(如JOIN、子查询),并与执行引擎和存储引擎集成。
- 优化SQL查询的执行计划,减少计算和I/O成本。
- 支持SQL ANSI 92标准的复杂查询(如JOIN、GROUP BY)。
- 提供高效的查询性能,超越嵌入式数据库(如H2、SQLite)。
- 与存储引擎协作,利用索引和统计信息。
- 计划生成:生成多种可能的执行计划。
- 成本估算:基于统计信息评估计划成本。
- 计划选择:选择最低成本的执行计划。
- 优化规则:应用谓词下推、JOIN顺序调整等优化。
查询计划(QueryPlan):
1
2
3
4
| public abstract class QueryPlan {
abstract double estimateCost();
abstract List<Map<String, Object>> execute(StorageEngine storage, long txId);
}
|
操作符(Operator):
- 包括扫描(Scan)、过滤(Filter)、连接(Join)等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public class ScanOperator extends QueryPlan {
private String tableName;
private double rowCount; // 统计信息
public ScanOperator(String tableName, double rowCount) {
this.tableName = tableName;
this.rowCount = rowCount;
}
@Override
double estimateCost() { return rowCount; }
@Override
List<Map<String, Object>> execute(StorageEngine storage, long txId) {
return storage.scanTable(tableName, txId);
}
}
|
- 成本因子:
- 扫描成本:表行数。
- 过滤成本:选择率(selectivity)。
- 连接成本:笛卡尔积大小和索引使用情况。
- 统计信息:
- 存储在
MetadataManager
中,包括行数、列选择率和索引信息。
- 规则优化:谓词下推(push down predicates)。
- 成本优化:动态规划选择最优JOIN顺序。
以下是QueryOptimizer
类的核心实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
| package com.yinlongfei.lightweight.database.optimizer;
import com.yinlongfei.lightweight.database.execution.SelectStatement;
import com.yinlongfei.lightweight.database.metadata.MetadataManager;
import com.yinlongfei.lightweight.database.storage.StorageEngine;
import java.util.*;
public class QueryOptimizer {
private final MetadataManager metadataManager;
private final StorageEngine storage;
public QueryOptimizer(MetadataManager metadataManager, StorageEngine storage) {
this.metadataManager = metadataManager;
this.storage = storage;
}
public QueryPlan optimize(SelectStatement stmt, long txId) {
List<QueryPlan> plans = generatePlans(stmt);
return plans.stream()
.min(Comparator.comparingDouble(QueryPlan::estimateCost))
.orElseThrow(() -> new RuntimeException("No valid plan generated"));
}
private List<QueryPlan> generatePlans(SelectStatement stmt) {
List<QueryPlan> plans = new ArrayList<>();
String tableName = stmt.getTableName();
double rowCount = metadataManager.getRowCount(tableName);
// 基本计划:扫描 + 过滤
QueryPlan scan = new ScanOperator(tableName, rowCount);
QueryPlan filter = stmt.getWhereClause() != null
? new FilterOperator(scan, stmt.getWhereClause())
: scan;
plans.add(filter);
// 如果有索引,生成索引扫描计划
List<String> indexedColumns = metadataManager.getIndexedColumns(tableName);
if (stmt.getWhereClause() != null && indexedColumns.contains(stmt.getWhereClause().getColumn())) {
plans.add(new IndexScanOperator(tableName, stmt.getWhereClause()));
}
// TODO: 支持JOIN和子查询计划
return plans;
}
}
class FilterOperator extends QueryPlan {
private final QueryPlan child;
private final Condition condition;
private static final double SELECTIVITY = 0.1; // 默认选择率
public FilterOperator(QueryPlan child, Condition condition) {
this.child = child;
this.condition = condition;
}
@Override
double estimateCost() {
return child.estimateCost() * SELECTIVITY;
}
@Override
List<Map<String, Object>> execute(StorageEngine storage, long txId) {
return child.execute(storage, txId).stream()
.filter(row -> evaluateCondition(row, condition))
.collect(Collectors.toList());
}
private boolean evaluateCondition(Map<String, Object> row, Condition cond) {
Object value = row.get(cond.getColumn());
switch (cond.getOperator()) {
case ">": return ((Comparable) value).compareTo(cond.getValue()) > 0;
case "=": return value.equals(cond.getValue());
default: return false;
}
}
}
class IndexScanOperator extends QueryPlan {
private final String tableName;
private final Condition condition;
public IndexScanOperator(String tableName, Condition condition) {
this.tableName = tableName;
this.condition = condition;
}
@Override
double estimateCost() {
return 10.0; // 假设索引扫描成本较低
}
@Override
List<Map<String, Object>> execute(StorageEngine storage, long txId) {
return storage.indexScan(tableName, condition, txId);
}
}
|
- 存储引擎:提供扫描和索引查询接口。
- 元数据管理:获取表统计信息和索引定义。
- 执行引擎:调用优化后的计划执行查询。
调整StorageEngine
以支持索引扫描:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| public class StorageEngine {
// ... 其他代码 ...
public List<Map<String, Object>> indexScan(String tableName, Condition condition, long txId) {
BPlusTree index = indexes.get(tableName + "_" + condition.getColumn());
if (index != null) {
List<Row> rows = index.search(condition.getValue());
return rows.stream()
.filter(row -> txManager.isVisible(row, txId))
.map(Row::getColumns)
.collect(Collectors.toList());
}
return executeSelect(new SelectStatement(tableName, null, condition), txId);
}
public List<Map<String, Object>> scanTable(String tableName, long txId) {
return memoryTables.getOrDefault(tableName, Collections.emptyList()).stream()
.filter(row -> txManager.isVisible(row, txId))
.map(Row::getColumns)
.collect(Collectors.toList());
}
}
|
查询优化器的执行流程:
sequenceDiagram
participant E as 执行引擎
participant O as 查询优化器
participant S as 存储引擎
participant M as 元数据管理
E->>O: optimize(stmt, txId)
O->>M: getRowCount(table)
M-->>O: rowCount
O->>M: getIndexedColumns(table)
M-->>O: indexedColumns
O-->>E: QueryPlan
E->>S: execute(plan, txId)
S-->>E: List<Map>
- 流程说明:
- 执行引擎调用优化器生成计划。
- 优化器从元数据管理获取统计信息。
- 返回最优计划,执行引擎调用存储引擎执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
| MetadataManager metadata = new MetadataManager();
StorageEngine storage = new StorageEngine("jdbc:lightweight:memory", metadata, txManager);
TransactionManager txManager = new TransactionManager(storage, logger);
QueryOptimizer optimizer = new QueryOptimizer(metadata, storage);
metadata.addTable("users", Arrays.asList("id", "name"), Collections.singletonList("id"), 1000);
InsertStatement insert = new InsertStatement("users", Arrays.asList("id", "name"), Arrays.asList(1, "Alice"));
storage.executeInsert(insert, txManager.begin(), logger);
SelectStatement select = new SelectStatement("users", null, new Condition("id", "=", 1));
QueryPlan plan = optimizer.optimize(select, txManager.getCurrentTxId());
List<Map<String, Object>> result = plan.execute(storage, txManager.getCurrentTxId());
System.out.println(result); // 输出 [{id=1, name=Alice}]
|
下一篇文章将实现“元数据管理”,详细设计表结构和统计信息的存储与访问机制,进一步完善数据库功能。
第六篇实现了查询优化器,通过基于成本的优化策略生成高效执行计划,支持索引利用和过滤优化。Mermaid图展示了优化流程,与存储引擎的集成提升了查询性能。后续将完善元数据支持。