文章目录
摘要: 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 里有英文分号 这个问题,他也是能完美解决