cmu15445-project3 解析器与查询计划 实验总结

一、 实验内容

查询优化器是一个沟通语句SQL到具体执行逻辑的桥梁。

从query到具体执行,代码要经过如下的过程:

query parsing:从文本query得到一棵语法树,语法树等上下文被绑定(保存)到执行计划中

query optimization:从语法树生成优化的执行计划

query execution:按照执行计划调用执行器执行

底层执行:执行器调用事务管理器,执行并发控制,底层数据结构简化如project1,2

 

 

Parser是常规操作,用来生成语句AST;

Binder不重要,它只是用来让Planner拥有AST信息和数据库底层信息的;

Planner产生的Plan是一棵树,每个节点是一种操作;1条SQL语句可能产生很多个Plan,但是每个Plan只处理一个Table的数据

Optimizer是Project3的主体之一,它接收plan,输出优化后的plan。在Optimize接口中实现具体的逻辑。

Catalog图中未提及,保存了Table和Index的信息,正是一部分元数据存储的地方

 

背景知识:

       通常流程:Executor调用Transaction接口,执行增删改查操作;对外暴露Next接口,,并加入标记;执行器的执行过程是迭代器模型,又称火山模型(Volcano)或者流水线(Pipeline)模型;

实现的接口

Task #1 - Access Method Executors  实现SeqScan、Insert、Delete、IndexScan 算子。

Task #2 - Aggregation & Join Executors:实现Aggregation、NestedLoopJoin、NestedIndexJoin 三个算子。HashJoin不是必须的要求。

Task #3 - Sort + Limit Executors and Top-N Optimization:实现Sort,Limit算子,并实现TopN算子优化;

Leaderboard Task:为 Optimizer 实现新的优化规则,让稍微复杂的样例sql 语句执行得更快。

二、 实验FAQ

1.迭代器模式

迭代器的优点是什么?执行过程可以不用一次性查出所有数据,节约内存。

对于查询,一次Next得到一条数据,而且在火山模型下,可能是嵌套的节点由上到下调用Next;

而对于删除,修改,一次调用要执行完对所有记录的操作。

其他模式:向量化模型(Vectorization Model),不遵循一次Next产生一条数据的模式。

2.执行器关联变量

每个executor有exec_ctx_,保存主要的上下文变量,包括事务管理器TransactionManager,目录Catalog,锁管理器;

executor中的table存储的方式:Catalog目录保存了TableInfo,TableInfo里保存了实际的Table;

一些特有的变量,如Aggregation算子用在group_by中,查询计划plan保存了group_bys_的查询条件;

Schema作为输出的变量,表示数据的每个列属性

3. HashJoin、NestedLoopJoin

JOIN为连接操作,即两个表的笛卡尔积,对于A JOIN B, 若A有n个满足条件的行,B有m个,若不加任何条件限制,则查询结果集为nxm;

NestedLoopJoin,HashJoin为数据库内部连接方式

而内连接,左外连接,右外连接为SQL支持的功能, 通过连接方式实现这些功能

1)     我们有常用结论,以较小的表作为左表进行连接,是一个代价相对小的方式,此时该表称为驱动表;

2)     hash join(HJ)是一种用于equi-join(而anti-join就是使用NOT IN时的join)的技术。主要将小表、大表分别hash到桶中,最后比较哈希桶中的数据得到结果;

3)     当Join个数大于1个,如A JOIN B JOIN C的时候,即为嵌套查询;

4)大表、小表只是简化称呼,实际中可能操作的是临时表,是加了过滤条件的

4.执行计划优化

查询优化包括:

基于规则的优化(Rule-Based-Optimization,简称 RBO)

基于代价的优化(Cost-Based Optimization,简称 CBO)

 

找到OptimizeCustom,在其中多加一条实现自己的规则即可;

auto Optimizer::OptimizeCustom(const AbstractPlanNodeRef &plan) -> AbstractPlanNodeRef {

  auto p = plan;

  //所有的优化规则

  p = OptimizeMergeProjection(p);

  p = OptimizeMergeFilterNLJ(p);

  p = OptimizeNLJAsIndexJoin(p);

  // p = OptimizeNLJAsHashJoin(p);  // Enable this rule after you have implemented hash join.

  p = OptimizeOrderByAsIndexScan(p);

  p = OptimizeSortLimitAsTopN(p);

  return p;

}

Query 1: Where's the Index?

SELECT * FROM (t1 INNER JOIN t2 ON t1.x = t2.x) INNER JOIN t3 ON t2.y = t3.y;

其中表t1有索引,我们需要利用到索引加快查询速度,但是原有的查询优化只能利用右表的索引;对于此,可以根据索引的位置交换JOIN的顺序;

Query 2: Too Many Joins!

SELECT * FROM t4, t5, t6

WHERE (t4.x = t5.x) AND (t5.y = t6.y) AND (t4.y >= 1000000)

AND (t4.y < 1500000) AND (t6.x >= 100000) AND (t6.x < 150000);

 

 

可以看到Filter在JOIN的上方,要将其进行下推,提前进行Filter操作,避免处理过多的无用数据;具体做法是将Filter节点下移到JOIN的下方;

 

Query 3: The Mad Data Scientist

SELECT v, d1, d2 FROM (

SELECT v,

MAX(v1) AS d1, MIN(v1), MAX(v2), MIN(v2),

MAX(v1) + MIN(v1), MAX(v2) + MIN(v2),

MAX(v1) + MAX(v1) + MAX(v2) AS d2

FROM t7 LEFT JOIN (SELECT v4 FROM t8 WHERE 1 == 2) ON v < v4 GROUP BY v
)

这个查询里有很多重复的字段,未经优化会产生很多重复计算;

要进行列裁剪;包括将重复的字段进行合并;

5.测试

project3的测试主要是手工测试,通过写好的SQL在shell里执行,直接查看生成的数据是否正确;这里没有必要写成单测的形式,因为相较于前面的测试来说这个实验的代码确实略微简单;

用如下命令测试:

cd build && make -j$(nproc) shell

./bin/bustub-shell

make -j$(nproc) sqllogictest

./bin/bustub-sqllogictest ../test/sql/p3.00-primer.slt --verbose

References

[1]自动测评网站 GradeScope,course entry code: PXWVR5 https://www.gradescope.com/

[2] https://15445.courses.cs.cmu.edu/fall2022 课程官网

[3] https://github.com/cmu-db/bustub Bustub Github Repo

[4] Database System Concepts 6th version, Abraham.Silberschatz.

 

posted @ 2023-08-21 23:04  stackupdown  阅读(40)  评论(0编辑  收藏  举报