开发者社区> dasein58> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

TiDB 源码学习:关于 Projection Pruning 的细节问题

简介: 查询优化器发现节点之间是 Proj/Aggr --> Proj 模式的时候(也就是某个 Proj 节点的祖先是 Proj或 Aggr 节点的时候),会考虑对子节点做 Projection Pruning 优化。 是否可以消除 Proj 节点的判断依据是:当前的 Proj 节点输出的列是否和其子节点的输出列一样。如果一样,则可以消除。 让我产生疑问的地方是判断输出列是否和子节点一样的代码逻辑,代码如下: func canProjectionBeEliminatedLoose(p *LogicalProjection) bool { for _, expr := range p.Ex
+关注继续查看

查询优化器发现节点之间是 Proj/Aggr --> Proj 模式的时候(也就是某个 Proj 节点的祖先是 Proj或 Aggr 节点的时候),会考虑对子节点做 Projection Pruning 优化。

是否可以消除 Proj 节点的判断依据是:当前的 Proj 节点输出的列是否和其子节点的输出列一样。如果一样,则可以消除。

让我产生疑问的地方是判断输出列是否和子节点一样的代码逻辑,代码如下:

func canProjectionBeEliminatedLoose(p *LogicalProjection) bool {

for _, expr := range p.Exprs {
    _, ok := expr.(*expression.Column)
    if !ok {
        return false
    }
}
return true

}
代码其实很简单,就是判断每个 Expr 是否是 *expression.Column 类型。根据 LogicalProjection (下面简称 LP)数据结构定义:

type LogicalProjection struct {

logicalSchemaProducer

Exprs []expression.Expression

// ...

}
LP 和其他一些逻辑查询节点类型一样,继承了 logicalSchemaProducer ,logicalSchemaProducer 保存列信息(参考 Schema 数据结构)。除此以外,LogicalProjection 还包含了 Exprs 字段。

我的疑问就是 要知道 Column 也是 Expression 类型,LogicalProjection 中 Exprs 字段和 schema.Columns 字段保存的信息不重复吗?二者有什么区别呢?

先说结论
对于很多简单查询来说 Exprs 和 schema.Columns 其实没啥区别,可以简单认为它们的信息就是一样的,比如下面这些查询:

select a from t;
select count(a) from t;
但是,对于 fields 中带有函数计算的查询语句(比如:select a + b as c from t),Exprs 字段中对应的数据类型就不再是 expression.Column 类型了, a+b 列也不是 LP 的子节点产生的,这类 LP 是不能被消除的。所以上面的代码逻辑简洁又正确。

具体代码分析
回顾代码中 LP 节点的创建过程,LP 是在 buildProjection 函数中创建出来的:

func (b PlanBuilder) buildProjection(p LogicalPlan, fields []ast.SelectField, mapper map[*ast.AggregateFuncExpr]int, considerWindow bool) (LogicalPlan, int, error) {

//...
proj := LogicalProjection{Exprs: make([]expression.Expression, , len(fields))}.Init(b.ctx)
schema := expression.NewSchema(make([]*expression.Column, , len(fields))...)
oldLen := 
for i, field := range fields {
    //...
    newExpr, np, err := b.rewrite(field.Expr, p, mapper, true)
    //...

    p = np
    proj.Exprs = append(proj.Exprs, newExpr)

    col, err := b.buildProjectionField(proj.id, schema.Len()+1, field, newExpr)
    //...
    schema.Append(col)
}
proj.SetSchema(schema)
proj.SetChildren(p)
return proj, oldLen, nil

}
代码中:

调用 rewrite 函数将 field.Expr 转换成 newExpr
将 newExpr 加入到 Exprs 字段中
调用 buildProjectionField 方法,使用 field/newExpr 创建新的列信息,加入到 schema 中
rewrite 函数是一个关键点,rewrite 过程涉及到了一些语法树和查询计划树中的数据结构,这里不做详细介绍。接下来,我们通过几个常见查询场景来说明一下 rewrite 函数起到的作用。

场景一:简单的查询语句

select a, b from t;
a 和 b 两个 field (*ast.SelectField)中的 Expr 类型都是 ast.ColumnNameExpr,rewrite 在处理这种类型的 field 时,其实就是找到它们的列名,从子节点的 schema 中找到对应的列(expression.Column 类型)并返回。

这类 field 在 rewrite 之后产生的 newExpr 是 *expression.Column 类型,这类语句中 LP 的 Exprs 和 schema.Columns 可以简单认为是一样的,没差别。

场景二:带有 Aggregation 的查询语句

select count(a) from t;
这种场景看似更复杂一些,其实 count(a) 这个 field 的 Expr 字段类型和QQ号码卖号场景一其实一样,也是 ast.ColumnNameExpr。rewrite 的处理过程基本没区别。

这个场景产生的查询计划是 DataSource -> LogicalAggregation -> LogicalProjection,count(a) 这一列其实是 LogicalAggregation 子节点提供的,LP 拿来用就行了。所以这类场景下,Exprs 和 schema.Columns 也没啥区别。

场景三:

select a + b as c from t;
在 fields 中有函数计算时,显然 LP 的子节点(在这里是 DataSource)的 schema 中是不可能包含这一列的。从这个场景下,Exprs 和 schema.Columns 就有所区别了。a+b field 中的 Expr 字段类型是ast.BinaryOperationExpr ,rewrite 的具体处理流程:

将 ast.BinaryOperationExpr 的 left 子节点和 right 子节点记录下来
根据 ast.BinaryOperationExpr 的函数类型找到对应的 ScalarFunction 类型
根据 step 1 和 step 2 中的信息,将该 field 转换成 expression.ScalarFunction 并返回
因此,a+b field 产生的 newExpr 并不是 Column 类型而是 ScalarFunction。

关于 rewrite

rewrite 函数在构建逻辑查询计划时作用很大,逻辑也比较复杂,不过在 Projection Pruning 优化中作用比较小,比较复杂的逻辑其实没有涉及到,特别是在上面提到的几个查询语句中。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
源码学习:探究MongoDB - ObjectId最新的生成原理
源码学习:探究MongoDB - ObjectId最新的生成原理
56 0
android studio添加project libs库步骤
在Eclipse中选择要导出的项目,然后依次选择菜单 file->export->Android->Generate Gradle build files. 之后依次点击next到finish即可   回到Android studio 打开settings.gradle,添加include ':libraries:你
1321 0
android studio添加project libs库步骤
<div style="line-height:28px; color:rgb(95,101,108); font-family:'Hiragino Sans GB W3','Hiragino Sans GB',Arial,Helvetica,simsun,u5b8bu4f53; font-size:16px"> <span style="color:#ff0000"><span sty
1210 0
Android ListView Adapter的getItemViewType和getViewTypeCount多种布局
 《Android ListView Adapter的getItemViewType和getViewTypeCount多种布局》 在Android的ListView中,如果在一个ListView中要实现多种样式的ListView布局样式,则需要在ListView的适配器Adapter中用到:getItemViewType()和getViewTypeCount()。
993 0
android 2.2 源码
1.android 2.2系统源码:  http://iask.sina.com.cn/u/1406363505/ish?folderid=905662.系统目录  http://hi.baidu.com/ydt_lwj/blog/item/5864f0aec53984d67dd92a36.
503 0
Android——permission之android:protectionLevel
permission 又一个头疼的问题 研究了一整天哦! 首先protectionLevel分为四级: "normal" The default value. A lower-risk permission that gives requesting applications acce...
736 0
honeynet project tools
http://old.honeynet.org/tools/index.html   牛逼的数据分析 http://old.
724 0
+关注
932
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载