Apache DolphinScheduler系列7-SQL任务因注释报错及经验总结


摘要: Apache DolphinScheduler系列7-SQL任务因注释报错及经验总结

关键词: 大数据、SQL任务、数据调度、注释报错

整体说明

在调研了 DolphinScheduler 之后,在项目上实际使用了一段时间,遇到了SQL任务因为注释的原因报错了,解决思路分享如下。

一、问题背景

  • SQL模板开头有规范注释

    首先介绍下,我们编写 SQL 的模板规范,在写 SQL 之前,有大段的注释,用来介绍这段SQL是用来生成什么的,编写人和创建时间等信息,

    如下截图所示:

  • 使用SQL模块执行任务

    我们使用 DolphinScheduler 的 SQL组件,把前面的那段 SQL 放入组件,希望周期性调度执行这段 SQL 来生成表。

二、问题现象

  • 执行报错,找不到表

    执行我们刚刚配置的任务,报错,找不到表

    执行日志如下:

三、问题分析

3.1、任务日志分析

  • 简化任务,排除干扰

    之前的任务太长,太复杂,可能性太多,经过一些列的尝试,最终简化任务 SQL 如下,仍然能复现问题

    --
    select * from test_yyjg
    
  • 开头放注释原因

    后台执行 SQL 日志截图如下

    报错信息虽然变了,现象是一样的,就是把这段 SQL 当成注释了,所以执行失败。

3.2、源码分析

3.2.1、SQL执行源码分析测试

  • SQL 执行源码

    找到 SQL 执行源码 ,发现就是使用基础的 SQL 执行模块

    截图如下:

  • 创建测试模块

    自己创建测试模块,测试日志内任务是否有问题

    代码如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

public class JdbcTest {

        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement pstmt = null;
            ResultSet rs = null;

            try {
                // 1. 加载数据库驱动(以MySQL为例)
                Class.forName("com.mysql.cj.jdbc.Driver");

                // 2. 获取数据库连接(替换为你的数据库信息)
                String url = "jdbc:mysql://********************";
                String user = "******";
                String password = "******";
                conn = DriverManager.getConnection(url, user, password);

                // 3. 准备SQL
                String sql = "-- \r\nselect * from test_yyjg;\r\n";
                
                // 4. 创建PreparedStatement
                pstmt = conn.prepareStatement(sql);

                // 5. 执行查询
                rs = pstmt.executeQuery();

                // 6. 处理结果集
                while (rs.next()) {
                    // 根据你的表结构获取字段值
                    String id = rs.getString("app_id");
                    String name = rs.getString("app_name");
                    System.out.println("app_id: " + id + ", app_name: " + name);
                }

            } catch (ClassNotFoundException e) {
                System.err.println("数据库驱动未找到!");
                e.printStackTrace();
            } catch (SQLException e) {
                System.err.println("数据库操作异常!");
                e.printStackTrace();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
}

​ 执行结果如下:

​ 发现基础的 SQL 执行模块,并没有因为注释的问题,而不识别后续的 SQL

3.2.2、SQL分段切分源码分析测试

  • 分段切分源码

    如果不是 SQL 执行问题,那很有可能就是 SQL 分段切分的问题了,

    分段切分字符串源码截图如下:

    分段逻辑源码如下:

  • 创建测试模块

    自己创建测试模块,测试分段切分是否有问题

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.List;
    
    
    public class JdbcTest {
    
            public static void main(String[] args) {
                Connection conn = null;
                PreparedStatement pstmt = null;
                ResultSet rs = null;
    
                try {
                    // 1. 加载数据库驱动(以MySQL为例)
                    Class.forName("com.mysql.cj.jdbc.Driver");
    
                    // 2. 获取数据库连接(替换为你的数据库信息)
                    String url = "jdbc:mysql://********************";
                    String user = "******";
                    String password = "******";
                    conn = DriverManager.getConnection(url, user, password);
    
                    // 3. 准备SQL(已清理注释和分号)
                    String sql = "-- \r\nselect * from test_yyjg;\r\n";
    //                String sql = "select * from test_yyjg";
    
                    String[] lines = sql.split(";\r\n");
    
                    for (String line : lines) {
                        if (line.trim().isEmpty() || line.startsWith("--")) {
                            continue;
                        }
                        System.out.printf("###: " + line);
                    }
    
                } catch (ClassNotFoundException e) {
                    System.err.println("数据库驱动未找到!");
                    e.printStackTrace();
                } catch (SQLException e) {
                    System.err.println("数据库操作异常!");
                    e.printStackTrace();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                
            }
        
    }
    

​ 执行结果发现并没有打印出 SQL,可以确定是这个分段 SQL 逻辑问题。

​ 截图如下:

3.3、分析结论

执行 SQL 不能以 “–” 开头,否则以 “–” 开头,直到 “;\r\n” 之间的 SQL 都会被解析成注释。

四、解决方案

4.1、临时方案

这个方案比较简单,就是避免以 “–” 开头,且 “;\r\n” 这个分号换行之后,也不要使用 “–”

4.2、完全解决

那就是修改源码,我们使用的版本是 3.2.0-release,后面已经有了 新版本,

我发现新版本已经解决了这个问题,郁闷的同时,也在感叹,DolphinScheduler 社区还是很活跃的

新方案描述:引入 alibaba.druid 来解决多段 SQL 解析的问题,而是简单的以 分隔符 切分

  • 源码

    源码解决方案截图如下:

  • 创建测试模块

    自己创建测试模块,验证新方案是否可用,引入 alibaba.druid

    import com.alibaba.druid.sql.parser.SQLParserUtils;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.List;
    
    public class JdbcTest {
    
            public static void main(String[] args) {
                Connection conn = null;
                PreparedStatement pstmt = null;
                ResultSet rs = null;
    
                try {
                    // 1. 加载数据库驱动(以MySQL为例)
                    Class.forName("com.mysql.cj.jdbc.Driver");
    
                    // 2. 获取数据库连接(替换为你的数据库信息)
                    String url = "jdbc:mysql://********************";
                    String user = "******";
                    String password = "******";
                    conn = DriverManager.getConnection(url, user, password);
    
                    // 3. 准备SQL(已清理注释和分号)
                    String sql = "-- \r\nselect * from test_yyjg;\r\n";
    
                    List<String> strings = splitAndRemoveComment(sql);
                    for (String s : strings) {
                        System.out.printf("@@@: " + s);
                    }
    
                } catch (ClassNotFoundException e) {
                    System.err.println("数据库驱动未找到!");
                    e.printStackTrace();
                } catch (SQLException e) {
                    System.err.println("数据库操作异常!");
                    e.printStackTrace();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                
            }
    
        public static List<String> splitAndRemoveComment(String sql) {
            return SQLParserUtils.splitAndRemoveComment(sql, com.alibaba.druid.DbType.other);
        }
        
    }
    

​ 执行结果,发现能够得到想要的 SQL ,并没有被当成注释

五、最终解决

项目上,因为软件升级流程复杂的原因,选择了暂时忍受了这个 bug ~,使用人员注意写法即可 (客户的问题十万火急,开发体验的问题能忍就忍,反正决策忍的人不是真正使用的人~)。

六、经验总结

  • 不要轻视SQL分段难度

    作为 Apache 的顶级项目,迭代到 3.2.0 版本还有这种低级的问题,我想可以归结为,都轻视了 SQL分段的难度,觉得这个只需要简单的关键字切分就可以了,

    事实上,我们公司现在的软件也有类似的问题,简单的使用分号作为分割多段 SQL 的依据,当我们的建表语句里的 comment 里有英文分号,也会导致报错,

    当然也不只是个例,我记得我上次使用开源工具 Apache Zeppelin 也有类似的问题。

  • 不要重复造轮子,使用成熟工具

    这就不得不提 alibaba.druid,这个工具相对还是成熟的,在 SQL 解析这块,我刚刚又去测试了一下 建表语句里的 comment 里有英文分号 这个问题,他也是能完美解决

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鹏说大数据

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

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

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

打赏作者

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

抵扣说明:

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

余额充值