1、 问题

如何解析SQL,并且获取SQL中关键数据?

  • 操作类型 (删除、插入、更新、添加字段)
  • 库名
  • 表名

2、 背景

数据治理项目中,监控业务库人工提单操作数据库的SQL,提前发现可能对数仓跑批或者数据质量造成危害的操作。

危害操作如下:

  • 导致【数据同步丢失】的操作:
    • 数据删除:数仓跑批不能发现,导数据一致性出现问题
    • 数据更新:不能有指定 updatetime 字段,会导致数仓同步数据丢失,数据一致性出现问题
    • 数据插入:同上,不能指定 updatetime
  • 导致【表发现失败】的操作:
    • 重命名表:导致数仓ETL操作无法查到表名报错
    • 重命名字段:同上,无法查询到字段名
    • 删字段:同上
  • 导致【ods表结构同步不及时】的操作(非危险操作,正常业务发展都可能添加字段):
    • 添加字段:业务库添加了字段,ods层需要及时发现重做

理论上业务库是不允许有删数据、删表、重命名等操作。

根据以上逻辑,需要从提单的SQL中发现

  • 操作类型
  • 库名
  • 表名

当解析出的表是数仓ods层同步过的表,同时操作类型是我们关注的类型,发出告警通知!

3、 方案预选

  1. 粗糙的方案:解析关键字,获取操作类型

    1. 优点:简单,方便,甚至粗暴
    2. 缺点:无法获取表名,不能解决我们需要判断是否同步到了数仓的功能
  2. 词法分析SQL解析器

    1. 优点:能解析出各种操作类型、库名、表名等
    2. 缺点:自己开发,可能没有这个能力

综上:最终选取了github上的一个项目,能解析出我们需要的三个关键属性
https://github.com/melin/bigdata-sql-parser

4、 方案实施

打包且install 到mvn里之后,Java项目就能使用了

伪代码如下:

  1. import com.github.bigdata.sql.parser.*;
  2. import com.github.bigdata.sql.parser.tidb.TidbSQLHelper;
  3. import java.util.ArrayList;
  4. String sql = "select col from db_name.table_name ";
  5. StatementData statementData = TidbSQLHelper.getStatementData(sql);
  6. //打印statementData,详见下一个代码块
  7. System.out.print(statementData.toString())
  8. Statement statement = statementData.getStatement();
  9. // 获取操作类型
  10. StatementType statementType = statementData.getType();
  11. ArrayList<com.github.bigdata.sql.parser.TableSource> inputTables = ((TableData) statement).getInputTables();
  12. TableSource tableSource = inputTables.get(0);
  13. //获取库名
  14. String database = tableSource.getDatabaseName();
  15. //获取表名
  16. String tableName = tableSource.getTableName();

statementData 打印结果如下:

  1. StatementData(type=SELECT, statement=TableData(inputTables=[TableSource(databaseName=db_name, tableName=table_name, column=null, columns=[])], outpuTables=[], limit=null))

需要注意的是,不同的数据库操作类型获取库名和表名的方法不一样

5、 应用拓展

  • 判断业务库SQL变更是否对数仓表产生影响(本文)
  • 对用户提交的SQL进行鉴权,判断是否有该表操作权限
  • 数仓血缘关系

其他应用需求欢迎补充