Pig源码分析: 逻辑执行计划优化

Whole View

本文分析的是逻辑执行计划优化的代码结构,具体每种Rule的实现不做分析。

看本文之前最好参考之前那篇逻辑执行计划模型的文章。

Architecture

几个关键类/接口的关系:

每个关键类/接口的实现和继承结构在下面各节展开。

Optimizer

PlanOptimizer是抽象类,主要和Rule、PlanTransformListener、OperatorPlan打交道。

public abstract class PlanOptimizer {

    protected List<Set<Rule>> ruleSets;
    protected OperatorPlan plan;
    protected List<PlanTransformListener> listeners;
    protected int maxIter;

它接受一个OperatorPlan,即Operators的DAG模型,在optimize()方法里,遍历ruleSet,得到几批Rules,即Set<Rule>。对于每批Rules,调用每个rule.match(plan)来处理传入的OperatorPlan,返回一个匹配成功的List<OperatorPlan> matches,对这些match的plans进行进一步处理。首先获得rule的transformer,然后进行transformer的check()和transform()操作。如果需要Listener操作的,还会遍历listeners,让每个PlanTransformListener监听到transformer进行的transform操作,transformer的reportChanges()方法可以返回他transform操作修改的部分。

代码如下:

    public void optimize() throws FrontendException {

        for (Set<Rule> rs : ruleSets) {
            boolean sawMatch = false;
            int numIterations = 0;
            do {
                sawMatch = false;
                for (Rule rule : rs) {
                    List<OperatorPlan> matches = rule.match(plan);
                    if (matches != null) {
                        Transformer transformer = rule.getNewTransformer();
                        for (OperatorPlan m : matches) {
                            try {
                                if (transformer.check(m)) {
                                    sawMatch = true;
                                    transformer.transform(m);
                                    if (!rule.isSkipListener()) {
                                        for(PlanTransformListener l: listeners) {
                                            l.transformed(plan, transformer.reportChanges());
                                        }
                                    }
                                }
                            } catch (Exception e) {
                                StringBuffer message = new StringBuffer("Error processing rule " + rule.name);
                                if (!rule.isMandatory()) {
                                    message.append(". Try -t " + rule.name);
                                }
                                throw new FrontendException(message.toString(), 2000, e);
                            }
                        }
                    }
                }
            } while(sawMatch && ++numIterations < maxIter);
        }
    }

实现类:

LogicalPlanOptimizer

LogicalPlanOptimizer类是PlanOptimizer的子类

 

默认加载两个Listener:

Listener的这两个实现在PlanTransformerListener一节具体展开讲述。

 

初始化的时候会buildRuleSets(),把需要添加的Rule都生成出来,然后校对该Rule是否被强制加入,或被turn off,从而选择性地放入优化规则。以下列举了所有候选的优化规则,Rule是顺序执行的:

Transformer

Transformer是抽象类,有三个方法需要子类实现:

check()方法,利用pattern来匹配plan里符合的operator集合,返回match的operator集

transform()方法,具体实施对tree的转换操作

reportChanges()方法,报告tree的哪部分被transform操作过了(只包括被修改了的或增加了的,不包括删除的node),目的是为了让Listener得知,从而可以修改schema或annotation等等。

 

继承结构如下:

PlanTransformerListener

PlanTransformListener监听一个plan被修改后会触发。

举例:

当一个Rule把一次join里的Filter步骤提前到join操作之间做,那么过滤部分的input schema很可能需要改变,此时一个schema listener就会被触发并执行。

 

PlanTransformListener是一个接口,需要实现一个方法:

public void transformed(OperatorPlan fp, OperatorPlan tp) throws FrontendException;

下面具体介绍两个实现类

ProjectionPatcher

作用是在映射操作中修补引用信息

有两个内部静态类

SchemaPatcher

使用于逻辑执行计划优化过程,当plan被transform了之后修补schema信息。

Rule

public abstract class Rule {
    protected String name = null;
    protected OperatorPlan pattern;
    transient protected OperatorPlan currentPlan;
private transient Set<Operator> matchedNodes =
new HashSet<Operator>();
    private boolean mandatory;
    private boolean skipListener = false;

Rule已经把 match(OperatorPlan plan)方法的逻辑实现好了。

子类需要实现的是buildPattern()方法,来制定各自的”模式”,即pattern变量。

子类还需要实现getNewTransformer()方法来实例化一个transformer,transformer的check()和transform()方法会进一步处理rule匹配的operators。

 

Rulematch()的用途是确保plan的所有子plan都满足该rule的pattern。

实现逻辑比较繁杂。

Rule继承结构

具体每个Rule不分析了。

全文完 :)

时间: 2024-09-23 10:50:29

Pig源码分析: 逻辑执行计划优化的相关文章

Pig源码分析: 逻辑执行计划模块

Whole View 本文分析的是Pig Logical模块的代码(newplan package下),具体每种逻辑执行的实现类不会做具体分析. Architecture 关键类/接口关系图 下面对关键类/接口具体实现做分析 Operator public abstract class Operator { protected SourceLocation location; // The location of the operator in the original pig script.

Pig源码分析: 简析执行计划的生成

摘要 本文通过跟代码的方式,分析从输入一批Pig-latin到输出物理执行计划(与launcher引擎有关,一般是MR执行计划,也可以是Spark RDD的执行算子)的整体流程. 不会具体涉及AST如何解析.如何使用了Anltr.逻辑执行计划如何映射.逻辑执行计划如何优化.MR执行计划如何切分为MR Job,而是从输入一批Pig DSL到待执行的真正执行计划的关键变化步骤(方法和类). 执行计划完整解析 入口处书Main类的main函数 /** * The Main-Class for the

MySQL · 源码分析 · 词法分析及其性能优化

Table of Contents 1. 简介 2. 背景知识 3. 查找树的实现 3.1. 树的查找 3.2. 树的产生 4. 试试折半查找 5. 总结 简介 MySQL 支持标准的 SQL 语言,具体实现的时候必然要涉及到词法分析和语法分析.早期的程序可能会优先考虑手工实现词法分析和语法分析,现在大多数场合下都会采用工具来简化实现.MySQL.PostgreSQL 等采用 C/C++ 实现的开源数据库采用的是现代的 yacc/lex 组合,也就是 GNU bison/flex.其他比较流行的

Spring源码分析——JdbcTemplate执行批量insert操作

最近用到一个方法: @Override public int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss) throws DataAccessException { if (logger.isDebugEnabled()) { logger.debug("Executing SQL batch update [" + sql + "]"); } return execute(sql

Django源码分析之执行入口

魔法门 一般我们启动django,最简单的方法是进入project 目录,这时目录结构是这样的 然后我们执行python manage.py runserver,程序就开始执行了. 那django是如何从一个命令就启动整个server,启动的流程是如何的? 踏门而入 打开目录下的manage.py,内容是这样的: #!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdef

PgSQL · 源码分析 · PG优化器物理查询优化

在之前的一篇月报中,我们已经简单地分析过PG的优化器(PgSQL · 源码分析 · PG优化器浅析),着重分析了SQL逻辑优化,也就是尽量对SQL进行等价或者推倒变换,以达到更有效率的执行计划.本次月报将会深入分析PG优化器原理,着重物理查询优化,包括表的扫描方式选择.多表组合方式.多表组合顺序等. 表扫描方式 表扫描方式主要包含顺序扫描.索引扫描以及Tid扫描等方式,不同的扫描方式 Seq scan,顺序扫描物理数据页 postgres=> explain select * from t1 ;

Catalyst 优化逻辑执行计划规则

Optimizer 本文分析Catalyst Optimize部分实现的对逻辑执行计划(LogicalPlan)的处理规则. Optimizer处理的是LogicalPlan对象. Optimizer的batches如下: object Optimizer extends RuleExecutor[LogicalPlan] { val batches = Batch("ConstantFolding", Once, ConstantFolding, // 可静态分析的常量表达式 Boo

Appium Android Bootstrap源码分析之命令解析执行

通过上一篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>我们知道了Appium从pc端发送过来的命令如果是控件相关的话,最终目标控件在bootstrap中是以AndroidElement对象的方式呈现出来的,并且该控件对象会在AndroidElementHash维护的控件哈希表中保存起来.但是appium触发一个命令除了需要提供是否与控件相关这个信息外,还需要其他的一些信息,比如,这个是什么命令?这个就是我们这篇文章需要讨论的话题了. 下面我

通过分析SQL语句的执行计划优化SQL_MsSql

如何干预执行计划 - - 使用hints提示 基于代价的优化器是很聪明的,在绝大多数情况下它会选择正确的优化器,减轻了DBA的负担.但有时它也聪明反被聪明误,选择了很差的执行计划,使某个语句的执行变得奇慢无比.此时就需要DBA进行人为的干预,告诉优化器使用我们指定的存取路径或连接类型生成执行计划,从而使语句高效的运行.例如,如果我们认为对于一个特定的语句,执行全表扫描要比执行索引扫描更有效,则我们就可以指示优化器使用全表扫描.在Oracle中,是通过为语句添加hints(提示)来实现干预优化器优