Flink源码分析:Flink SQL Submit源码提交过程

系列文章

Flink源码分析:基于事件时间场景下WaterMark源码分析

Flink源码分析: 重启策略机制RestartStrategy

Flink源码分析: 广播状态流实现动态更新或字段参数变更

Flink源码分析: Flink JDBC Upsert模式实现原理

目录

系列文章

一. SqlClient.class

加载外部jar包

加载lib包下的jar包

创建一个本地执行器

用执行器加载默认环境创建客户端

客户端中打开一个新的会话

执行SQL

总结

关注收藏不迷路~~

一. SqlClient.class

我们打开Flink Sql启动客户端脚本sql-client.sh,从中看出脚本主要是调用了

org.apache.flink.table.client.SqlClient这个类。
# start client without jar
    exec $JAVA_RUN $JVM_ARGS "${log_setting[@]}" -classpath "`manglePathList "$CC_CLASSPATH:$INTERNAL_HADOOP_CLASSPATHS"`" org.apache.flink.table.client.SqlClient "$@"

 我们找到SqlClient.class这个类,首先SQL Client作用是为了用户提交SQL语句,并且支持两种模式:embedded mode 和 gateway mode (Flink1.12.0版本不支持getway模式,下面的源码里写了)。

创建SqlClient对象时会调用start()类型,这里主要是封装了SqlClient的主要SQL提交操作。

private void start() {
        if (isEmbedded) {
            // create local executor with default environment
            final List<URL> jars;
            if (options.getJars() != null) {
                jars = options.getJars();
            } else {
                jars = Collections.emptyList();
            }
            final List<URL> libDirs;
            if (options.getLibraryDirs() != null) {
                libDirs = options.getLibraryDirs();
            } else {
                libDirs = Collections.emptyList();
            }
            final Executor executor = new LocalExecutor(options.getDefaults(), jars, libDirs);
            executor.start();

            // create CLI client with session environment
            final Environment sessionEnv = readSessionEnvironment(options.getEnvironment());
            appendPythonConfig(sessionEnv, options.getPythonConfiguration());
            final SessionContext context;
            if (options.getSessionId() == null) {
                context = new SessionContext(DEFAULT_SESSION_ID, sessionEnv);
            } else {
                context = new SessionContext(options.getSessionId(), sessionEnv);
            }

            // Open an new session
            String sessionId = executor.openSession(context);
            try {
                // add shutdown hook
                Runtime.getRuntime()
                        .addShutdownHook(new EmbeddedShutdownThread(sessionId, executor));

                // do the actual work
                openCli(sessionId, executor);
            } finally {
                executor.closeSession(sessionId);
            }
        } else {
            throw new SqlClientException("Gateway mode is not supported yet.");
        }
    }

start()私有方法主要干了以下几件事:

  • 加载外部jar包
  • 加载lib包下的jar包
  • 创建一个本地执行器
  • 用执行器加载默认环境创建客户端
  • 客户端中打开一个新的会话
  • 执行SQL

加载外部jar包

是一个外部连接的集合,这里的Jar包可以是程序所需要的的依赖,也可以是自定义函数

加载lib包下的jar包

是一个外部连接的集合,这里的Jar包可以是程序所需要的的依赖,也可以是自定义函数

创建一个本地执行器

LocalExecutor对象的作用主要是用来提交程序以及返回执行结果,本地执行器会加载上文的外部Jar包以及默认的配置文件。

用执行器加载默认环境创建客户端

从CliOptions里加载环境信息,初始化一个默认环境给执行器创建客户端,这个默认环境由系统默认的SessionID决定。

客户端中打开一个新的会话

这里没什么好讲的,源码里很简单

执行SQL

我们来看下执行SQL的过程,主要封装在openCli方法中,该方法需要传入两个参数:

String sessionId, Executor executor

当所有环境配置都准备好,会执行以下代码提交SQL语句:

try (CliClient cli = new CliClient(sessionId, executor, historyFilePath)) {
            // interactive CLI mode
            if (options.getUpdateStatement() == null) {
                cli.open();
            }
            // execute single update statement
            else {
                final boolean success = cli.submitUpdate(options.getUpdateStatement());
                if (!success) {
                    throw new SqlClientException(
                            "Could not submit given SQL update statement to cluster.");
                }
            }
        }

可以看出关键点在

cli.submitUpdate(options.getUpdateStatement())

点进去看下submitUpdate源码

public boolean submitUpdate(String statement) {
        terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_WILL_EXECUTE).toAnsi());
        terminal.writer().println(new AttributedString(statement).toString());
        terminal.flush();

        final Optional<SqlCommandCall> parsedStatement = parseCommand(statement);
        // only support INSERT INTO/OVERWRITE
        return parsedStatement
                .map(
                        cmdCall -> {
                            switch (cmdCall.command) {
                                case INSERT_INTO:
                                case INSERT_OVERWRITE:
                                    return callInsert(cmdCall);
                                default:
                                    printError(CliStrings.MESSAGE_UNSUPPORTED_SQL);
                                    return false;
                            }
                        })
                .orElse(false);
    }

 这里一个很关键的地方:

final Optional<SqlCommandCall> parsedStatement = parseCommand(statement);

这条代码的作用很神奇,它可以把SQL语言转成Table API来执行,这就是我们常说的数据库的解析过程,从上层的SQL解析成底层代码API执行,接着跟进这段代码:

private Optional<SqlCommandCall> parseCommand(String line) {
        final SqlCommandCall parsedLine;
        try {
            parsedLine = SqlCommandParser.parse(executor.getSqlParser(sessionId), line);
        } catch (SqlExecutionException e) {
            printExecutionException(e);
            return Optional.empty();
        }
        return Optional.of(parsedLine);
    }

 接着跟进SqlCommandParse这个类,来看看这个类的结构:

 看到了吧,SQL的常规语法全部在里面,String类型的SQL字符串会在这里做解析成语法树,这已经是Table API层面的代码了。由于篇幅太长,会在下期具体讲解SQL是如何解析的。

到这里一个SQL语句通过客户端提交流程已经完全讲完了,再次帮大家总结下SQL提交过程:

加载Jar包(jar/lib包下) -> 创建本地执行器 -> 初始化环境 -> 创建一个新的会话 -> 提交SQL -> 提交后交给Table API层面的解析器 -> 生成语法树


总结

下面一章节会给大家讲解Flink SQL在解析层面的源码是如果走的,记得关注收藏哦!!

在Apache Spark中,`spark.sql.shuffle.partitions`是一个非常重要的配置参数,它用于指定执行Shuffle操作时要创建的分区数。这个参数对于Spark作业的性能有重要的影响。 当Spark的作业在执行需要Shuffle的操作时,比如join、group by、reduce by等操作,需要在各个不同的执行节点之间进行数据的重新分配。此时,数据需要跨节点进行传输,因此合理的Shuffle分区数可以优化数据的传输和处理。 如果Shuffle分区数设置得太小,可能会导致以下问题: 1. 每个分区的数据量过大,可能会引起内存不足的问题,因为每个节点上要处理的数据量增加了。 2. 任务执行不均衡,可能会导致某些节点的负载较重,而其他节点则相对较轻。 3. 减少并行度,影响作业的总体执行效率。 如果Shuffle分区数设置得太大,可能会导致以下问题: 1. 每个分区的数据量很小,可能会造成资源使用效率低,因为需要更多的任务和调度开销。 2. 增加了小任务的数量,可能使得任务的启动和管理开销变得显著。 3. 对于一些操作,比如排序和聚合,过多的分区意味着需要更多的内存来维护分区的数据。 通常情况下,最佳的Shuffle分区数取决于集群的规模和资源、作业的具体需求以及数据的大小。可以通过实验和监控来调整这个参数,找到适合当前作业和集群的最优值。 在使用`spark-submit`命令提交Spark作业时,可以通过设置`spark.sql.shuffle.partitions`来指定这个参数的值。具体做法是在提交作业的命令中加入`--conf`参数来指定配置,例如: ``` spark-submit --class com.example.MyApp --master yarn --conf spark.sql.shuffle.partitions=200 my-app.jar ``` 在这个例子中,我们设置了Shuffle分区数为200。用户可以根据实际需要调整这个数值以达到最佳的作业性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XuTengRui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值