【源码解析】flink sql执行源码概述:flink sql执行过程中有哪些阶段,这些阶段的源码大概位置在哪里

news2024/12/24 8:46:25

文章目录

  • 一. sql执行流程源码分析
    • 1. Sql语句解析成语法树阶段(SQL - > SqlNode)
    • 2. SqlNode 验证(SqlNode – >Operation)
    • 3. 语义分析(Operation - > RelNode)
    • 4. 优化阶段(RelNode - > optimize - >Transformation )
    • 5. 生成ExecutionPlan并执行
  • 二. 源码分析小结
    • `sqlnode->relnode->优化->pipeline(StreamGraph)-> 执行并返回结果`

本文大致分析了flink sql执行过程中的各个阶段的源码逻辑,这样可以在flink sql执行过程中, 能够定位到任务执行的某个阶段的代码大概分布在哪里,为更针对性的分析此阶段的细节逻辑打下基础,比如create 的逻辑是怎么执行的,select的逻辑是怎么生成的,优化逻辑都做了哪些,而这些是接下来的文章要分析的。

一. sql执行流程源码分析

SQL语句经过Calcite解析生成抽象语法树SQLNode,基于生成的SQLNode并结合flink Catalog完成校验生成一颗Operation树,接下来blink planner将Opearation树转为RelNode树然后进行优化,最后进行执行。如下流程流转图:

在这里插入图片描述
 

flink使用的是一款开源SQL解析工具Apache Calcite ,Calcite使用Java CC对sql语句进行了解析,转换为java/scala语言能够执行的逻辑。

 
Sql 的执行过程一般可以分为下图中的四个阶段,Calcite 同样也是这样:解析,校验,优化,执行:
在这里插入图片描述

1. Sql语句解析成语法树阶段(SQL - > SqlNode)

对于flink中解析sql为SqlNode对象的流程为:

  • TableEnvironmentImpl是sql执行的入口类,TableEnvironmentImpl中提供了excuteSqlSqlQuery等方法用来执行DDL、DML等sql
  • sql执行时会先对sql进行解析,ParserImp是flink调用sql解析的实现类,ParserImpl#parse()方法中通过调用包装器对象CalciteParser#parse()方法创建并调用使用javacc生成的sql解析器
  • (FlinkSqlParserImpl)parseSqlStmtEof方法完成sql解析,并返回SqlNode对象。

具体calciteParser 的动作之后更新

在这里插入图片描述

parse方法:负责将 SQL 查询字符串解析为抽象语法树(AST)

org.apache.flink.table.planner.delegation.ParserImpl
    /**
     * When parsing statement, it first uses {@link ExtendedParser} to parse statements. If {@link
     * ExtendedParser} fails to parse statement, it uses the {@link CalciteParser} to parse statements.
     * 先使用ExtendedParser进行解析如果解析失败了使用CalciteParser进行解析
     * @param statement input statement.
     * @return parsed operations.
     */
    @Override
    public List<Operation> parse(String statement) {
        //两种解析实例
        CalciteParser parser = calciteParserSupplier.get();
        FlinkPlannerImpl planner = validatorSupplier.get();
        //ExtendedParser解析
        Optional<Operation> command = EXTENDED_PARSER.parse(statement);
        if (command.isPresent()) {
            return Collections.singletonList(command.get());
        }
        //CalciteParser解析
        //解析为sqlNode
        SqlNodeList sqlNodeList = parser.parseSqlList(statement);
        List<SqlNode> parsed = sqlNodeList.getList();
        Preconditions.checkArgument(parsed.size() == 1, "only single statement supported");
        return Collections.singletonList(
                //解析为SOperation
                SqlToOperationConverter.convert(planner, catalogManager, parsed.get(0))
                        .orElseThrow(() -> new TableException("Unsupported query: " + statement)));
    }

将sql语句解析成sqlNode。
对应的表名、列名、with属性参数、主键、唯一键、分区键、水印、表注释、表操作(create table、alter table、drop table。。。)都放到SqlNode对象的对应属性中,SqlNode是一个树形结构也就是AST。
在这里插入图片描述

 

2. SqlNode 验证(SqlNode – >Operation)

经过上面的第一步,会生成一个 SqlNode 对象,它是一个未经验证的抽象语法树,下面就进入了一个语法检查阶段,语法检查前需要知道元数据信息,这个检查会包括表名、字段名、函数名、数据类型的检查。

  • sql解析完成后执行sql校验,flink sql中增加了SqlNode转换为Operation的过程,sql校验是这个过程中完成。
  • 在SqlToOperationConvertver#convert()方法中完成这个转换,之后通过FlinkPlannerImpl#validate()方法对表、函数、字段等完成校验并基于生成的validated SqlNode生成对应的Operation。

在这里插入图片描述

接着进入到SqlToOperationConverter.convert方法中

    /**
     * This is the main entrance for executing all kinds of DDL/DML {@code SqlNode}s, different
     * SqlNode will have it's implementation in the #convert(type) method whose 'type' argument is
     * subclass of {@code SqlNode}.
     * 转换DDL,DML(select比如?)sqlnode的主入口,不同的SqlNode有不同的convert实现。
     * 
     */
    public static Optional<Operation> convert(
            FlinkPlannerImpl flinkPlanner, CatalogManager catalogManager, SqlNode sqlNode) {
        //1.进行校验
        final SqlNode validated = flinkPlanner.validate(sqlNode);
        //2.转换为Operation
        return convertValidatedSqlNode(flinkPlanner, catalogManager, validated);
    }
 
   //将校验过的sqlnode转换为Operation
   private static Optional<Operation> convertValidatedSqlNode(
            FlinkPlannerImpl flinkPlanner, CatalogManager catalogManager, SqlNode validated) {
        SqlToOperationConverter converter =
                new SqlToOperationConverter(flinkPlanner, catalogManager);
	     。。。
	     else if (validated instanceof RichSqlInsert) {
	            return Optional.of(converter.convertSqlInsert((RichSqlInsert) validated));
	     } else if (validated.getKind().belongsTo(SqlKind.QUERY)) {
	            return Optional.of(converter.convertSqlQuery(validated));

但其实converter.convertSqlQuery包含了createSqlToRelConverter逻辑,即创建了SqlToRelConverter实例,用于转换RelNode。这里源码暂不展示。

 

3. 语义分析(Operation - > RelNode)

接着将 SqlNode 转换成 RelNode/RexNode,也就是生成相应的逻辑计划(Logical Plan),也就是最初版本的逻辑计划(Logical Plan)。

其中Operation中包含了RelNode的converter

源码见:org.apache.flink.table.planner.delegation.PlannerBase

  /** Converts a relational tree of [[ModifyOperation]] into a Calcite relational expression.
  将ModifyOperation转换为Calcite relational expression即RelNode
   */
  @VisibleForTesting
  private[flink] def translateToRel(modifyOperation: ModifyOperation): RelNode = {
    val dataTypeFactory = catalogManager.getDataTypeFactory
    modifyOperation match {
      case s: UnregisteredSinkModifyOperation[_] =>
        //relBuilder:
        val input = createRelBuilder.queryOperation(s.getChild).build()
        val sinkSchema = s.getSink.getTableSchema
        //校验relnode的 查询schema和sink schema是否一致,以及是否需要执行cast
        // validate query schema and sink schema, and apply cast if possible
        val query = validateSchemaAndApplyImplicitCast(
          input,
          catalogManager.getSchemaResolver.resolve(sinkSchema.toSchema),
          null,
          dataTypeFactory,
          getTypeFactory)
        LogicalLegacySink.create(
          query,
          s.getSink,
          "UnregisteredSink",
          ConnectorCatalogTable.sink(s.getSink, !isStreamingMode))
....

这里触发创建relnode的调用逻辑,这里在之后statementSet.execute()后执行。

//执行flink sql的调用
。。。
//解析sql -> sqlnode
StatementSet statementSet = SqlParser.parseSql(job, jarUrlList, tableEnv);
//sqlnode->relnode
TableResult execute = statementSet.execute();
	tableEnvironment.executeInternal(operations);
		TableEnvironmentImpl.translate
			PlannerBase.translate->translateToRel			

 

4. 优化阶段(RelNode - > optimize - >Transformation )

即对逻辑计划优化,根据前面生成的逻辑计划按照相应的规则进行优化。

接着看tableEnvironment.executeInternal中的translate方法

  override def translate(
      modifyOperations: util.List[ModifyOperation]): util.List[Transformation[_]] = {
    beforeTranslation()
    。。。
    //转换为relnode并放到一个map中
    val relNodes = modifyOperations.map(translateToRel)
    //优化逻辑计划
    val optimizedRelNodes = optimize(relNodes)
    //生成execGraph:执行图
    val execGraph = translateToExecNodeGraph(optimizedRelNodes, isCompiled = false)
    //生成transformations DAG
    val transformations = translateToPlan(execGraph)
    afterTranslation()
    transformations
  }

Calcite 的核心所在,优化器进行优化的地方,如过滤条件的下压(push down),在进行 join 操作前,先进行 filter 操作,这样的话就不需要在 join 时进行全量 join,减少参与 join 的数据量等。

 

5. 生成ExecutionPlan并执行

最终的执行计划转为Graph图,下面的流程与真正的java代码流程就一致了。

TableEnvironmentImpl.executeInternal中具体看executeInternal方法

TableEnvironmentImpl.executeInternal(
...
TableResultInternal result = executeInternal(transformations, sinkIdentifierNames);
...
)


    private TableResultInternal executeInternal(
            List<Transformation<?>> transformations, List<String> sinkIdentifierNames) {
         。。。
         //Translates the given transformations to a Pipeline.
         //将transformations转换为pipeline
        Pipeline pipeline =
                execEnv.createPipeline(
                        transformations, tableConfig.getConfiguration(), defaultJobName);
        try {
            // 执行pipeline
            //pipeline 其实就是StreamGraph
            JobClient jobClient = execEnv.executeAsync(pipeline);
         。。。
            }
		 。。。
		 //执行后返回结果
            return TableResult。。。

    }

通过生成的Transformation对象调用 execEnv.createPipeline,生成pipelinepipeline其实就StreamGraph便可以调用execEnv.executeAsync执行任务了。

 

二. 源码分析小结

上述描述了flink sql在内部执行过程进行的一些操作:
在这里插入图片描述

这里我们从执行sql tEnv.executeSql(stmt)statementSet.addInsertSql(sql); parse、validate阶段)生成statementSet,然后执行statementSet.execute()optimize、Execute阶段) 触发任务执行。

 

parse、validate阶段

通过执行以下代码触发

StatementSet statementSet = SqlParser.parseSql(job, jarUrlList, tableEnv);

statementSet.addInsertSql(sql);
tEnv.executeSql(stmt)

通过调用 tEnv.executeSql(stmt)statementSet.addInsertSql(sql); 进行每个sql的解析,校验,具体:

  1. Sql语句解析成语法树阶段(SQL - > SqlNode)
  2. SqlNode 验证(SqlNode – >Operation),其中Operation中包含着RelNode的convert实例,为转换逻辑计划做提前准备

 
optimize、Execute阶段

接着执行如下代码

//sqlnode->relnode->优化->pipeline(StreamGraph)-> 执行并返回结果
TableResult execute = statementSet.execute();
//调用链:
	tableEnvironment.executeInternal(operations);
		TableEnvironmentImpl.translate
			PlannerBase.translate->translateToRel

经历如下几个过程:

sqlnode->relnode->优化->pipeline(StreamGraph)-> 执行并返回结果

 

这里我们主要看executeInternal的逻辑

    public TableResultInternal executeInternal(List<ModifyOperation> operations) {
        List<ModifyOperation> mapOperations = new ArrayList<>();
        for (ModifyOperation modify : operations) {
            //1.先执行CTAS sql语句, 并放到mapOperations中进行translate操作
            // execute CREATE TABLE first for CTAS statements
            if (modify instanceof CreateTableASOperation) {
                CreateTableASOperation ctasOperation = (CreateTableASOperation) modify;
                executeInternal(ctasOperation.getCreateTableOperation());
                mapOperations.add(ctasOperation.toSinkModifyOperation(catalogManager));
            } else {
               //2. 将其他非CTAS sqlnode放到mapOperations,进行translate操作
                mapOperations.add(modify);
            }
        }
        //translate主要的逻辑是:将所有的sqlNodes转换为relNodes,为初始的逻辑计划,然后优化逻辑计划,
        //接着翻译 ExecNodeGraph 为 Transformation DAG.
        List<Transformation<?>> transformations = translate(mapOperations);
        List<String> sinkIdentifierNames = extractSinkIdentifierNames(mapOperations);
        //  transformations转换为pipeline,最终执行pipeline即StreamGraph,然后返回结果
        TableResultInternal result = executeInternal(transformations, sinkIdentifierNames);
        if (tableConfig.get(TABLE_DML_SYNC)) {
            try {
                result.await();
            } catch (InterruptedException | ExecutionException e) {
                result.getJobClient().ifPresent(JobClient::cancel);
                throw new TableException("Fail to wait execution finish.", e);
            }
        }
        return result;
    }

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1308233.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uni-app 一些实用的页面模板

时间倒计时 <!-- 时间倒计时 --> <template><view class"container"><view class"flex-row time-box"><view class"time-item">{{ laveTimeList[0] }}</view><text>天</text><view class&qu…

喜讯 | 同立海源生物入选2023年国创中心细胞疗法“揭榜挂帅”技术攻关项目

近日&#xff0c;2023年国家生物药技术创新中心细胞疗法“揭榜挂帅”技术攻关拟立项目名单公示&#xff0c;北京同立海源生物科技有限公司&#xff08;简称“同立海源生物”&#xff09;参评的 “细胞分选激活磁珠研发项目” 凭借公司多年在细胞分选磁珠领域的技术沉淀和创新性…

【期末考复习向】transformer的运作机制

1.transformer的encoder运作 transformer的encoder部分包括了输入和处理2大部分。首先是输入部分inputs&#xff0c;这里初始的inputs是采用独热向量进行表示的&#xff0c;随后经过word2vec等操作把独热向量&#xff08;采用独热向量的好处就是可向量是正交的&#xff0c;可以…

K8S(五)—命名空间与资源配额

目录 命名空间(Namespace)命令计算资源配额创建命名空间绑定一个ResourceQuota资源将命名空间和资源限制对象进行绑定尝试创建第二个 Pod查看ResourceQuota 绑定第二个ResourceQuota为命名空间配置默认的 CPU 、memory请求和限制&#xff08;1&#xff09;Pod 中所有容器都没有…

样本空间的一个划分

假设试验E的样本空间为S&#xff0c;为E的一组事件&#xff0c;如果这组事件满足如下条件&#xff1a; &#xff0c;其中&#xff0c;&#xff0c;即事件两两之间互不相容 那么就称为样本空间S的一个划分。

3D Font

在游戏中使用3D文本 只需添加预制件并立即生成您的文本。 特点: *真实3D字母&#xff0c;可用作游戏对象*移动友好低聚 *VR兼容 *WebGL兼容 *30种以上不同字体 *材料和颜色可定制 WebGL演示 https://indiechest.itch.io/3d-font-engine 下载&#xff1a; ​​Unity资源商店链…

redis:二、缓存击穿的定义、解决方案(互斥锁、逻辑过期)的优缺点和适用场景、面试回答模板

缓存击穿的定义 缓存击穿是一种现象&#xff0c;具体就是某一个数据过期时&#xff0c;恰好有大量的并发请求过来&#xff0c;这些并发的请求可能会瞬间把DB压垮。典型场景就是双十一等抢购活动中&#xff0c;首页广告页面的数据过期&#xff0c;此时刚好大量用户进行请求&…

Linux的五种IO模型

众所周知&#xff0c;出于对 OS 安全性的考虑&#xff0c;用户进程是不能直接操作 I/O 设备的。必须通过系统调用请求操作系统内核来协助完成 I/O 动作。 下图展示了 Linux I/O 的过程。 操作系统内核收到用户进程发起的请求后&#xff0c;从 I/O 设备读取数据到 kernel buff…

使用PM2,在生产环境稳定运行你的node项目

PM2 一个 node&#xff0c;本身就用几行代码&#xff0c;就可以启动个 server 进程&#xff0c;监听个端口&#xff0c;为大家提供 Web 服务 一、依赖安装 npm install pm2 -g 二、命令行启动 普通执行启动 pm2 start <js 文件路径 >.js 携带参数启动 pm2 start < 某种…

AR眼镜_AR智能眼镜整机硬件方案定制

AR眼镜的主要模块包括显示、光学模组、传感器和摄像头、主板、音频和网络连接等。其中&#xff0c;光学显示、主板处理器是决定AR眼镜成本的关键&#xff0c;光机占整体AR眼镜成本43%、处理器占整体成本31%。 AR眼镜的主板设计难点在于尺寸要足够小且要处理好散热问题。主板上的…

Fuzz入门教学——污点分析

1、简介 污点分析是一种用于检测和防止安全漏洞的技术。它关注数据流中的敏感信息&#xff08;污点&#xff09;如何在程序中传播&#xff0c;从而导致安全风险。这种分析通常用于发现潜在的安全漏洞&#xff0c;例如隐私数据泄露或者对数据完整性的威胁。污点分析标记程序中的…

集成显卡Intel® UHD Graphics 630 安装Pytorch

安装Pytorch是为了搭建maskrcnn环境。 查了一圈发现&#xff1a; 显卡Intel UHD Graphics 630是集成显卡&#xff0c;所以不能安装cuda和cudann&#xff0c;也不能使用GPU加速 所以也需要放弃在该电脑上的用VMware新建Ubuntu虚拟机&#xff0c;然后安装有cuda版本的Pytorch&a…

2023-12-08 队列与栈

栈与队列一 232. 用栈实现队列 思路&#xff1a;对于使用栈实现队列的话&#xff0c;必须使用两个共同来维护使得每次都能先进先出&#xff01; class MyQueue:def __init__(self):# 需要建立两个list来维护出栈以及进栈self.stack_in []self.stack_out []def push(self, x…

C# 两个日期比较大小

文章目录 C# 两个日期比较大小直接比较大小工具类DateTime.Compare C# 两个日期比较大小 直接比较大小 string ed "2023-12-13 09:27:59.000";//过去式DateTime nowDateTime DateTime.Now;DateTime expirationDate Convert.ToDateTime(ed);//质保期 长日期DateT…

压缩pdf怎么压缩又小又清晰?超级实用!

当PDF文件过大时&#xff0c;很容易遇到无法上传等现象&#xff0c;这时候我们可以借助一些专业的压缩工具&#xff0c;将PDF文件压缩变小&#xff0c;如果你还不知道怎么做&#xff0c;下面就来看下具体的压缩方法吧。 方法一&#xff1a;使用嗨格式压缩大师 1、打开电脑上安…

【Debug小结】关于部署Java项目的Jar包到本地服务器失败

问题描述&#xff1a; 在Idea中完成项目编码后&#xff0c;使用Package打包成.jar文件 接下来使用cmd执行java -jar xxx 命令后 报错如下&#xff1a;Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" j…

【内存函数】

目录 memcpy使用和模拟实现memmove使用和模拟实现memset使用memcmp使用 1. memcpy使用和模拟实现 void * memcpy ( void * destination, const void * source, size_t num) ; 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存的位置这个函数在遇到…

【Jenkins】Centos环境安装Jenkins(通过docker安装)

通过docker环境安装Jenkins 参考官网 https://hub.docker.com/r/jenkins/jenkins/ 1、安装docker环境 # 删除已有安装包 sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-…

企业考勤技术,这个方法很靠谱!

在当今社会&#xff0c;随着科技的不断进步&#xff0c;人脸识别技术在各个领域得到了广泛应用。其中&#xff0c;三维人脸考勤系统作为一种高效、准确的考勤解决方案&#xff0c;正在成为企业管理和人力资源管理的重要工具之一。 与传统的二维人脸识别相比&#xff0c;三维人脸…

Corona最新渲染器Corona11详解,附送下载地址

近日&#xff0c;Corona进行了大版本更新&#xff0c;发布了最新的Corona11。这次更新&#xff0c;包含众多新功能和新修复&#xff0c;借助 Corona 11 用户可将作品提升到更高的创作水准&#xff0c;更真实可感的视觉水平。 那么更新了那些呢&#xff1f;一起来看看吧&#x…