cover_image

SQL前置解析探索与实践

桜才 信也科技拍黑米
2025年03月26日 05:54

1 背景介绍

    在信也,数据驱动业务决策的理念贯穿始终,大数据分析在挖掘潜在价值、助力业务增长方面起着关键作用。而 SQL 作为大数据分析的核心工具之一,能够从海量数据中精准检索有价值的信息,为业务的精细化运营和战略规划提供有力支持,是实现大数据价值转化的重要环节。

    然而,在实际的业务场景中,SQL 的运算对集群资源有着较高的需求,而公司集群的资源总量存在上限。尤其是在业务繁忙的高峰时段,如每月的业务结算期、特定营销活动推广期间等,集群的空闲资源急剧减少。此时,未经优化的 SQL 语句会进一步加剧资源竞争,导致查询效率降低,甚至影响到关键业务系统的正常运行,给业务方带来不便,也可能造成潜在的业务损失。

    为了有效应对这一挑战,在信也,我们成功实现了一套基于 SQL 执行前的表级血缘分析与质量评分机制。通过对 SQL 语句进行提前分析,精准识别其中涉及的表之间的血缘关系,并对 SQL 质量进行量化评分,从而实现了资源峰值削峰与慢 SQL 提前拦截的双重目标。

2 技术介绍

    方案基于ANTLR4实现(ANother Tool for Language Recognition 4),它能依据特定的语法规则,把输入的文本转化为可处理的数据结构,让开发者可以轻松构建出编译器、解释器、语法检查器等。

    简单来说就是一种语法分析生成器,可以自定义语法规则,来达到分块解析的目的。

工作原理

  • 定义语法规则:开发者用 ANTLR4 提供的语法格式书写语法文件,精确描述某种语言的语法规则。
  • 生成解析器:ANTLR4 会依据定义好的语法文件,自动生成对应的词法分析器和语法分析器。
  • 处理输入文本:生成的分析器能够处理输入的文本,进行词法分析和语法分析,将文本转化成抽象语法树(AST)。
  • 定制操作:开发者可以编写代码,在抽象语法树的基础上进行语义分析、代码生成等操作。
    当输入一段SQL文本时,解析器会将文本转化成一棵解析树,如图所示。这样能将SQL文本原子化,通过树形结构可以获取任一文本,从而进一步达成其他解析目的。
图片
图1

实践案例

3.1 SQL表级血缘解析

    SQL文本中带有数据流向,例如insert into A表 select from B表这段SQL文本的数据流向为由B表到A表,解析SQL文本需要做到3点。

  1. 判断SQL语法属于什么类型create...select...语法和insert...select...语法会带有数据流向。
  2. 能够精准识别和获取SQL文本中的表名信息。
  3. 匹配公司主流SQL执行引擎:Hive、Impala、Spark、StarRocks。
关键实现
  • 引擎适配:针对 Hive/Impala 的 5% 语法差异,建立「通用语法库 + 方言补丁」机制(如 Hive 的复杂分区语法、Impala 的物化视图引用)。
  • 精准匹配:通过 AST 树遍历,捕获INSERT INTO A SELECT FROM B中的A→B流向,支持子查询嵌套场景(识别率 99.97%)。
图片
图2
接口案例
//传参SQL内容create table if not exists test.test_zyc03 asselect  t1.*from  test.test_zyc01 t1  left join test.test_zyc02 t2 on t1.userid = t2.userid;
//接口返回值{  "code": 200,  "msg": "成功",  "data": [    {      "sqlType": "create_as",  //sql语法类型        "outputTable": [         //输出表        {          "dbName": "test",          "tableName": "test_zyc03"        }      ],      "withTable": [],      "cacheTable": [],      "inputTables": [          //输入表        {          "dbName": "test",          "tableName": "test_zyc02"        },        {          "dbName": "test",          "tableName": "test_zyc01"        }      ],      "allTables": [        {          "dbName": "test",          "tableName": "test_zyc03"        },        {          "dbName": "test",          "tableName": "test_zyc02"        },        {          "dbName": "test",          "tableName": "test_zyc01"        }      ]    }  ]}

3.2 SQL评分 

3.2.1 第一层解析(SQL文本 + 元数据评分)

    利用Antlr4工具对SQL解析除了可以精准获取信息外,还有一大特点就是速度快,SQL的平均解析速度在20ms左右,获取SQL中的字段名,表名,分区字段等信息,再结合表的元数据信息,就可以在SQL执行前对当前SQL质量做一个判断。

    当用户执行一段SQL时,会先进行解析和打分操作并将结果返回给用户,返回内容包含扣分原因和修改建议,用户根据分值判断是否执行该段SQL。整个打分过程只需要消耗几十毫秒,可以在慢SQL运行前及时遏止,防止对于集群资源的大量消耗。首先把能够被收集到的信息罗列出来,查看哪些信息是可以被类用的。

  • SQL解析:表名、字段名称、筛选条件、join内容等
  • Hive元数据:字段数量、分区数量、字段和分区的统计信息等
打分规则

规则

触发条件

优化建议

全表扫描(SELECT *)

表字段 > 500 个

显式指定必要字段

分区表无分区过滤

WHERE 无分区字段

显式添加分区键范围过滤

笛卡尔积 JOIN

ON 条件缺失

补充 JOIN 字段

动态分区错位

SELECT 末列≠分区字段数

检查字段顺序与分区定义

实现

在指定好上述打分规则后,即可对SQL进行解析和打分,大致逻辑如下:

图片
图3

3.2.2 第二层解析(explain解析)

    只通过SQL文本和Hive元数据库中获取到的信息有限,还不足以涵盖所有的慢SQL查询场景。公司分析师使用的SQL引擎大多数是impala引擎,explain中含有更为信息的数据指标,并且执行explain会比正常执行快的多,如果能够将explain中的信息解析提取并加以利用,就可以更好的辅助SQL前置打分系统。第二层解析耗时为几十毫秒,两层解析可并行进行,将扣分结果合并后返回用户。

难点

    难点在于没有任何explain解析器可以参考,需要从零基于Antlr4工具进行研发,这里只针对implain引擎的explain文本进行了涉及与实现。

涉及思路

impala explain文本示例:

MAX PER-HOST RESOURCE RESERVATION: MEMORY=72.00MB THREADS=4PER-HOST RESOURCE ESTIMATES: MEMORY=316MBWARNING: THE FOLLOWING TABLES ARE MISSING RELEVANT TABLE AND/OR COLUMN STATISTICS.ODS.CPUANALYZED QUERY: SELECT DISTINCT (SN) FROM ODS.CPUF02:PLAN FRAGMENT [UNPARTITIONED] HOSTS=1 INSTANCES=1PER-HOST RESOURCES: MEM-ESTIMATE=10.06MB MEM-RESERVATION=0B THREAD-RESERVATION=1  PLAN-ROOT SINK  |  OUTPUT EXPRS:  (SN)   |  MEM-ESTIMATE=0B MEM-RESERVATION=0B THREAD-RESERVATION=0  |  04:EXCHANGE [UNPARTITIONED]     MEM-ESTIMATE=10.06MB MEM-RESERVATION=0B THREAD-RESERVATION=0     TUPLE-IDS=1 ROW-SIZE=12CARDINALITY=1.04G     IN PIPELINES: 03(GETNEXT)F01:PLAN FRAGMENT [HASH((SN))] HOSTS=4 INSTANCES=4PER-HOST RESOURCES: MEM-ESTIMATE=138.06MB MEM-RESERVATION=34.00MB THREAD-RESERVATION=1  DATASTREAM SINK [FRAGMENT=F02, EXCHANGE=04, UNPARTITIONED]  |  MEM-ESTIMATE=0B MEM-RESERVATION=0B THREAD-RESERVATION=0  03:AGGREGATE [FINALIZE]  |  GROUP BY:  (SN)   |  MEM-ESTIMATE=128.00MB MEM-RESERVATION=34.00MB SPILL-BUFFER=2.00MB THREAD-RESERVATION=0  |  TUPLE-IDS=1 ROW-SIZE=12CARDINALITY=1.04G  |  IN PIPELINES: 03(GETNEXT), 00(OPEN)  |  02:EXCHANGE [HASH((SN))]  MEM-ESTIMATE=10.06MB MEM-RESERVATION=0B THREAD-RESERVATION=0  TUPLE-IDS=1 ROW-SIZE=12CARDINALITY=1.04G  IN PIPELINES: 00(GETNEXT)F00:PLAN FRAGMENT [RANDOM] HOSTS=4 INSTANCES=4PER-HOST RESOURCES: MEM-ESTIMATE=168.00MB MEM-RESERVATION=38.00MB THREAD-RESERVATION=2  DATASTREAM SINK [FRAGMENT=F01, EXCHANGE=02, HASH((SN))]  |  MEM-ESTIMATE=0B MEM-RESERVATION=0B THREAD-RESERVATION=0  01:AGGREGATE [STREAMING]  |  GROUP BY:  (SN)   |  MEM-ESTIMATE=128.00MB MEM-RESERVATION=34.00MB SPILL-BUFFER=2.00MB THREAD-RESERVATION=0  |  TUPLE-IDS=1 ROW-SIZE=12CARDINALITY=1.04G  |  IN PIPELINES: 00(GETNEXT)  |  00:SCAN HDFS [ODS.CPU, RANDOM]  HDFS PARTITIONS=17/303 FILES=367 SIZE=9.40GB  STORED STATISTICS:  TABLEROWS=UNAVAILABLE SIZE=UNAVAILABLE  PARTITIONS: 17/17 ROWS=1.04G  COLUMNS:  UNAVAILABLE   EXTRAPOLATED-ROWS=DISABLED MAX-SCAN-RANGE-ROWS=2.98M  MEM-ESTIMATE=40.00MB MEM-RESERVATION=4.00MB THREAD-RESERVATION=1  TUPLE-IDS=0 ROW-SIZE=12CARDINALITY=1.04G  IN PIPELINES: 00(GETNEXT)

解析重点

  • 资源指标:单节点内存预估(如MEM-ESTIMATE=138MB)、扫描文件数(FILES=367)、分区覆盖比(PARTITIONS=17/303
  • 反模式识别
    • 大表广播(EXCHANGE [BROADCAST]且数据量 > 1GB)
    • 多层聚合(AGGREGATE [STREAMING]嵌套FINALIZE

解析器设计思路:

图片
图4
图片
图5
规则补充

规则

触发条件

优化建议

单表扫描过多的分区

分区数 > 1000个

SQL优化,减少扫描分区数量

单表扫描过多的文件

文件数 > 10000个

避免每个分区生成过多的小文件,合并小文件

广播过大的文件

广播表的大小超过阈值>1G

减少广播表的数据量,调整 Join 策略

SQL节点预估使用内存大小

使用内存大小>5G

优化聚合操作,拆分复杂查询

完整逻辑
图片
图6

4 未来计划

  1. 动态阈值:基于历史执行数据,自动调整评分阈值(如分区数阈值随表规模动态变化)
  2. 自动优化:实验 SQL 改写能力(如自动添加分区过滤、替换 SELECT *)

招聘信息

Java、大数据、前端、测试等各种技术岗位热招中,欢迎了解~

更多福利请关注官方订阅号信也科技拍黑米拍码场

喜欢请点击↓↓↓

继续滑动看下一个
信也科技拍黑米
向上滑动看下一个