老知識(shí)復(fù)盤-SQL從提交到執(zhí)行到底經(jīng)歷了什么
一、什么是SQL
sql(Structured Query Language: 結(jié)構(gòu)化查詢語(yǔ)言)是高級(jí)的費(fèi)過(guò)程化編程語(yǔ)言,允許用戶在高層數(shù)據(jù)結(jié)構(gòu)上工作, 是一種數(shù)據(jù)查詢和程序設(shè)計(jì)語(yǔ)言, 也是(ANSI)的一項(xiàng)標(biāo)準(zhǔn)的計(jì)算機(jī)語(yǔ)言. but... 目前仍然存在著許多不同版本的sql語(yǔ)言,為了與ANSI標(biāo)準(zhǔn)相兼容, 它們必須以相似的方式共同地來(lái)支持一些主要的命令(比如SELECT、UPDATE、DELETE、INSERT、WHERE等等).
在標(biāo)準(zhǔn)SQL中, SQL語(yǔ)句包含四種類型
DML(Data Manipulation Language):數(shù)據(jù)操作語(yǔ)言,用來(lái)定義數(shù)據(jù)庫(kù)記錄(數(shù)據(jù))。
DCL(Data Control Language):數(shù)據(jù)控制語(yǔ)言,用來(lái)定義訪問(wèn)權(quán)限和安全級(jí)別。
DQL(Data Query Language):數(shù)據(jù)查詢語(yǔ)言,用來(lái)查詢記錄(數(shù)據(jù))。
DDL(Data Definition Language):數(shù)據(jù)定義語(yǔ)言,用來(lái)定義數(shù)據(jù)庫(kù)對(duì)象(庫(kù),表,列等)
二、如何執(zhí)行SQL
2.1 mysql
以mysql為例, sql執(zhí)行流程大致分為以下節(jié)點(diǎn)(mysql server層代碼, 不包含引擎層事務(wù)/log等操作):

mysqlLex: mysql自身的詞法分析程序, C++語(yǔ)言開(kāi)發(fā), 基于輸入的語(yǔ)句進(jìn)行分詞, 并解析除每個(gè)分詞的意義. 分詞的本質(zhì)便是正則表達(dá)式的匹配過(guò)程. 源碼在sql/sql_lex.cc
Bision: 根據(jù)mysql定義的語(yǔ)法規(guī)則,進(jìn)行語(yǔ)法解析,語(yǔ)法解析就是生成語(yǔ)法樹的過(guò)程. 核心是如何涉及合適的存儲(chǔ)結(jié)構(gòu)以及相關(guān)算法,去存儲(chǔ)和遍歷所有的信息
語(yǔ)法解析中,生成語(yǔ)法樹:

mysql分析器: SQL解析, 針對(duì)關(guān)鍵詞/非關(guān)鍵詞進(jìn)行提取、解析, 并生成解析語(yǔ)法樹. 如果分析到語(yǔ)法錯(cuò)誤,會(huì)拋出異常: ERROR: You have an error in your SQL syntax. 同時(shí)該階段也會(huì)做一些校驗(yàn), 如不存在字段會(huì)拋出異常: unknow column in field list.
引申點(diǎn):
a. 語(yǔ)法樹生成規(guī)則
b. mysql的優(yōu)化規(guī)則
2.2 hive sql
Hive 是基于Hadoop 構(gòu)建的一套數(shù)據(jù)倉(cāng)庫(kù)分析系統(tǒng),它提供了豐富的SQL查詢方式來(lái)分析存儲(chǔ)在Hadoop 分布式文件系統(tǒng)中的數(shù)據(jù),可以將結(jié)構(gòu)化的數(shù)據(jù)文件映射為一張數(shù)據(jù)庫(kù)表,并提供完整的SQL查詢功能,可以將SQL語(yǔ)句轉(zhuǎn)換為MapReduce任務(wù)進(jìn)行運(yùn)行,通過(guò)自己的SQL 去查詢分析需要的內(nèi)容,這套SQL 簡(jiǎn)稱Hive SQL,使不熟悉mapreduce 的用戶很方便的利用SQL 語(yǔ)言查詢,匯總,分析數(shù)據(jù)
hive架構(gòu)圖:

Driver:
輸入了sql字符串,對(duì)sql字符串進(jìn)行解析,轉(zhuǎn)化程抽象語(yǔ)法樹,再轉(zhuǎn)化成邏輯計(jì)劃,然后使用優(yōu)化工具對(duì)邏輯計(jì)劃進(jìn)行優(yōu)化,最終生成物理計(jì)劃(序列化反序列化,UDF函數(shù)),交給Execution執(zhí)行引擎,提交到MapReduce上執(zhí)行(輸入和輸出可以是本地的也可以是HDFS/Hbase)見(jiàn)下圖的hive架構(gòu)
hiveSql的執(zhí)行流程如下:

sql寫出來(lái)以后只是一些字符串的拼接,所以要經(jīng)過(guò)一系列的解析處理,才能最終變成集群上的執(zhí)行的作業(yè)
(1)Parser:將sql解析為AST(抽象語(yǔ)法樹),會(huì)進(jìn)行語(yǔ)法校驗(yàn),AST本質(zhì)還是字符串
(2)Analyzer:語(yǔ)法解析,生成QB(query block)
(3)Logicl Plan:邏輯執(zhí)行計(jì)劃解析,生成一堆Opertator Tree
(4)Logical optimizer:進(jìn)行邏輯執(zhí)行計(jì)劃優(yōu)化,生成一堆優(yōu)化后的Opertator Tree
(5)Phsical plan:物理執(zhí)行計(jì)劃解析,生成tasktree
(6)Phsical Optimizer:進(jìn)行物理執(zhí)行計(jì)劃優(yōu)化,生成優(yōu)化后的tasktree,該任務(wù)即是集群上的執(zhí)行的作業(yè)
結(jié)論:經(jīng)過(guò)以上的六步,普通的字符串sql被解析映射成了集群上的執(zhí)行任務(wù),最重要的兩步是 邏輯執(zhí)行計(jì)劃優(yōu)化和物理執(zhí)行計(jì)劃優(yōu)化(圖中紅線圈畫)
Antlr: Antrl是一種語(yǔ)言識(shí)別的工具, 基于java開(kāi)發(fā), 可以用來(lái)構(gòu)造領(lǐng)域語(yǔ)言. 它提供了一個(gè)框架,可以通過(guò)包含java, C++, 或C#動(dòng)作(action)的語(yǔ)法描述來(lái)構(gòu)造語(yǔ)言識(shí)別器, 編譯器和解釋器.Antlr完成了hive 詞法分析、語(yǔ)法分析、語(yǔ)義分析、中間代碼生成的過(guò)程.
AST語(yǔ)法樹舉例:

引申學(xué)習(xí):
a. 從hivesql的執(zhí)行機(jī)制可以看出, hive并不適合用于聯(lián)機(jī)事務(wù)處理, 無(wú)法提供實(shí)時(shí)查詢功能;最適合應(yīng)用在基于大量不可變數(shù)據(jù)的批處理作業(yè)
b. Antlr的解析流程
c. hive的優(yōu)化規(guī)則
2.3 flink sql
Flink SQL是Flink中最高級(jí)的抽象, 可以劃分為 SQL --> Table API --> DataStream/DataSetAPI --> Stateful Stream Processing
Flink SQL包含 DML 數(shù)據(jù)操作語(yǔ)言、 DDL 數(shù)據(jù)語(yǔ)言, DQL 數(shù)據(jù)查詢語(yǔ)言,不包含DCL語(yǔ)言。

(1)首先,F(xiàn)linkSQL 底層使用的是 apache Calcite 引擎來(lái)處理SQL語(yǔ)句,Calcite會(huì)使用 javaCC 做SQL解析,javaCC根據(jù)Calcite中定義的 Parser.jj 文件,生成一系列的java代碼,生成的java代碼會(huì)把SQL轉(zhuǎn)換成AST抽象語(yǔ)法樹(即SQLNode類型)。
(2)生成的 SqlNode 抽象語(yǔ)法樹,他是一個(gè)未經(jīng)驗(yàn)證的抽象語(yǔ)法樹,這時(shí) SQL Validator 會(huì)獲取 Flink Catalog 中的元數(shù)據(jù)信息來(lái)驗(yàn)證 sql 語(yǔ)法,元數(shù)據(jù)信息檢查包括表名,字段名,函數(shù)名,數(shù)據(jù)類型等檢查。然后生成一個(gè)校驗(yàn)后的SqlNode。
(3)到達(dá)這步后,只是將 SQL 解析到 java 數(shù)據(jù)結(jié)構(gòu)的固定節(jié)點(diǎn)上,并沒(méi)有給出相關(guān)節(jié)點(diǎn)之間的關(guān)聯(lián)關(guān)系以及每個(gè)節(jié)點(diǎn)的類型信息。
所以,還需要將 SqlNode 轉(zhuǎn)換為邏輯計(jì)劃,也就是LogicalPlan,在轉(zhuǎn)換過(guò)程中,會(huì)使用 SqlToOperationConverter 類,來(lái)將 SqlNode 轉(zhuǎn)換為 Operation,Operation 會(huì)根據(jù)SQL語(yǔ)法來(lái)執(zhí)行創(chuàng)建表或者刪除表等操作,同時(shí)FlinkPlannerImpl.rel()方法會(huì)將SQLNode轉(zhuǎn)換成RelNode樹,并返回RelRoot。
(4)第4步將執(zhí)行 Optimize 操作,按照預(yù)定義的優(yōu)化規(guī)則 RelOptRule 優(yōu)化邏輯計(jì)劃。
Calcite 中的優(yōu)化器RelOptPlanner有兩種,一是基于規(guī)則優(yōu)化(RBO)的HepPlanner,二是基于代價(jià)優(yōu)化(CBO)的VolcanoPlanner。然后得到優(yōu)化后的RelNode, 再基于Flink里面的rules將優(yōu)化后的邏輯計(jì)劃轉(zhuǎn)換成物理計(jì)劃。
(5)第5步 執(zhí)行 execute 操作,會(huì)通過(guò)代碼生成 transformation,然后遞歸遍歷各節(jié)點(diǎn),將DataStreamRelNode 轉(zhuǎn)換成DataStream,在這期間,會(huì)依次遞歸調(diào)用DataStreamUnion、DataStreamCalc、DataStreamScan類中重寫的 translateToPlan方法。遞歸調(diào)用各節(jié)點(diǎn)的translateToPlan,實(shí)際是利用CodeGen元編成Flink的各種算子,相當(dāng)于直接利用Flink的DataSet或者DataStream開(kāi)發(fā)程序。
(6)最后進(jìn)一步編譯成可執(zhí)行的 JobGraph 提交運(yùn)行。
Flink SQL使用 Apache Calcite 作為解析器和優(yōu)化器
Calcite : 一種動(dòng)態(tài)數(shù)據(jù)管理框架,它具備很多典型數(shù)據(jù)庫(kù)管理系統(tǒng)的功能 如SQL 解析、 SQL 校驗(yàn)、 SQL 查詢優(yōu)化、 SQL 生成以及數(shù)據(jù)連接查詢等,但是又省略了一些關(guān)鍵的功能,如 Calcite并不存儲(chǔ)相關(guān)的元數(shù)據(jù)和基本數(shù)據(jù),不完全包含相關(guān)處理數(shù)據(jù)的算法等。
引申學(xué)習(xí):
a. flink sql優(yōu)化規(guī)則
三、常見(jiàn)SQL解析引擎
解析引擎 | 開(kāi)發(fā)語(yǔ)言 | 使用場(chǎng)景 | 總結(jié) |
antlr | java | presto | 1. 包含三大主要功能: 詞法分析器、語(yǔ)法分析器、樹解析器2. 支持定義領(lǐng)域語(yǔ)言 |
calcite | javaCC | flink | 1. 抽象語(yǔ)法樹2. 支持使用 FreeMarker 模版引擎擴(kuò)展語(yǔ)法3. 能夠與數(shù)據(jù)庫(kù)創(chuàng)建查詢 |
持續(xù)補(bǔ)充ing...
四、總結(jié)
在實(shí)際工作過(guò)程中會(huì)涉及到相關(guān)的sql優(yōu)化, 比如將非研發(fā)的業(yè)務(wù)老師寫的復(fù)雜嵌套sql后臺(tái)自動(dòng)改為非嵌套執(zhí)行,提高查詢性能. 支持redisSQL, 以標(biāo)準(zhǔn)SQL格式解析成后臺(tái)可執(zhí)行的redis命令. 目前采用的開(kāi)源jsqlparser框架來(lái)實(shí)現(xiàn)語(yǔ)法樹的解析, 好處是操作簡(jiǎn)單, 只對(duì)sql語(yǔ)句進(jìn)行拆分, 解析成java類的層次結(jié)構(gòu),支持visitor模式, 與數(shù)據(jù)庫(kù)無(wú)關(guān). 缺點(diǎn)是只支持常見(jiàn)的SQL語(yǔ)法集, 如若要擴(kuò)展語(yǔ)法需改其源碼, 對(duì)代碼的侵入性與維護(hù)性造成影響.想要做好sql解析優(yōu)化相關(guān)的工作, 還是要深入了解sql的執(zhí)行原理, 了解各個(gè)sql引擎的特點(diǎn)與優(yōu)劣. 站在架構(gòu)的角度來(lái)思考來(lái)思考問(wèn)題.
工欲善其事,必先利其器.
作者:京東科技 李丹楓
來(lái)源:京東云開(kāi)發(fā)者社區(qū) 轉(zhuǎn)載請(qǐng)注明來(lái)源





























