摘要:開(kāi)發(fā)一款能支持標(biāo)準(zhǔn)數(shù)據(jù)庫(kù)SQL的大數(shù)據(jù)倉(cāng)庫(kù)引擎,讓那些在Oracle上運(yùn)行良好的SQL可以直接運(yùn)行在Hadoop上,而不需要重寫(xiě)成Hive QL。
本文分享自華為云社區(qū)《???????????????從零開(kāi)發(fā)大數(shù)據(jù)SQL引擎??》,作者:JavaEdge 。
學(xué)習(xí)大數(shù)據(jù)技術(shù)的核心原理,掌握一些高效的思考和思維方式,構(gòu)建自己的技術(shù)知識(shí)體系。明白了原理,有時(shí)甚至不需要學(xué)習(xí),順著原理就可以推導(dǎo)出各種實(shí)現(xiàn)細(xì)節(jié)。
各種知識(shí)表象看雜亂無(wú)章,若只是學(xué)習(xí)繁雜知識(shí)點(diǎn),固然自己的知識(shí)面是有限的,并且遇到問(wèn)題的應(yīng)變能力也很難提高。所以有些高手看起來(lái)似乎無(wú)所不知,不論談?wù)撈鹗裁醇夹g(shù),都能頭頭是道,其實(shí)并不是他們學(xué)習(xí)、掌握了所有技術(shù),而是他們是在談到這個(gè)問(wèn)題時(shí),才開(kāi)始進(jìn)行推導(dǎo),并迅速得出結(jié)論。
高手不一定要很資深、經(jīng)驗(yàn)豐富,把握住技術(shù)的核心本質(zhì),掌握快速分析推導(dǎo)的能力,能迅速將自己的知識(shí)技能推到陌生領(lǐng)域,就是高手。
本系列專注大數(shù)據(jù)開(kāi)發(fā)需要關(guān)注的問(wèn)題及解決方案。跳出繁雜知識(shí)表象,掌握核心原理和思維方式,進(jìn)而融會(huì)貫通各種技術(shù),再通過(guò)各種實(shí)踐訓(xùn)練,成為終極高手。
大數(shù)據(jù)倉(cāng)庫(kù)Hive
作為一個(gè)成功的大數(shù)據(jù)倉(cāng)庫(kù),它將SQL語(yǔ)句轉(zhuǎn)換成MapReduce執(zhí)行過(guò)程,并把大數(shù)據(jù)應(yīng)用的門(mén)檻下降到普通數(shù)據(jù)分析師和工程師就可以很快上手的地步。
但Hive也有問(wèn)題,由于它使用自定義Hive QL,對(duì)熟悉Oracle等傳統(tǒng)數(shù)據(jù)倉(cāng)庫(kù)的分析師有上手難度。特別是很多企業(yè)使用傳統(tǒng)數(shù)據(jù)倉(cāng)庫(kù)進(jìn)行數(shù)據(jù)分析已久,沉淀大量SQL語(yǔ)句,非常龐大也非常復(fù)雜。某銀行的一條統(tǒng)計(jì)報(bào)表SQL足足兩張A4紙,光是完全理解可能就要花很長(zhǎng)時(shí)間,再轉(zhuǎn)化成Hive QL更費(fèi)力,還不說(shuō)可能引入bug。
開(kāi)發(fā)一款能支持標(biāo)準(zhǔn)數(shù)據(jù)庫(kù)SQL的大數(shù)據(jù)倉(cāng)庫(kù)引擎,讓那些在Oracle上運(yùn)行良好的SQL可以直接運(yùn)行在Hadoop上,而不需要重寫(xiě)成Hive QL。
Hive處理過(guò)程
- 將輸入的Hive QL經(jīng)過(guò)語(yǔ)法解析器轉(zhuǎn)換成Hive抽象語(yǔ)法樹(shù)(Hive AST)
- 將Hive AST經(jīng)過(guò)語(yǔ)義分析器轉(zhuǎn)換成MapReduce執(zhí)行計(jì)劃
- 將生成的MapReduce執(zhí)行計(jì)劃和Hive執(zhí)行函數(shù)代碼提交到Hadoop執(zhí)行
可見(jiàn),最簡(jiǎn)單的,對(duì)第一步改造即可??紤]替換Hive語(yǔ)法解析器:能將標(biāo)準(zhǔn)SQL轉(zhuǎn)換成Hive語(yǔ)義分析器能處理的Hive抽象語(yǔ)法樹(shù),即紅框代替黑框。
紅框內(nèi):淺藍(lán)色是個(gè)開(kāi)源的SQL語(yǔ)法解析器,將標(biāo)準(zhǔn)SQL解析成標(biāo)準(zhǔn)SQL抽象語(yǔ)法樹(shù)(SQL AST),后面深藍(lán)色定制開(kāi)發(fā)的SQL抽象語(yǔ)法樹(shù)分析與轉(zhuǎn)換器,將SQL AST轉(zhuǎn)換成Hive AST。
那么關(guān)鍵問(wèn)題就來(lái)了:
標(biāo)準(zhǔn)SQL V.S Hive QL
- 語(yǔ)法表達(dá)方式,Hive QL語(yǔ)法和標(biāo)準(zhǔn)SQL語(yǔ)法略有不同
- Hive QL支持的語(yǔ)法元素比標(biāo)準(zhǔn)SQL要少很多,比如,數(shù)據(jù)倉(cāng)庫(kù)領(lǐng)域主要的測(cè)試集TPC-H所有的SQL語(yǔ)句,Hive都不支持。尤其是Hive不支持復(fù)雜嵌套子查詢,而數(shù)據(jù)倉(cāng)庫(kù)分析中嵌套子查詢幾乎無(wú)處不在。如下SQL,where條件existes里包含了另一條SQL:
select o_orderpriority, count(*) as order_count
from orders
where o_orderdate >= date '[DATE]'
and o_orderdate < date '[DATE]' + interval '3' month
and exists
(select *
from lineitem
where l_orderkey = o_orderkey
and l_commitdate < l_receiptdate)
group by o_orderpriority
order by o_orderpriority;
開(kāi)發(fā)支持標(biāo)準(zhǔn)SQL語(yǔ)法的SQL引擎難點(diǎn),就是消除復(fù)雜嵌套子查詢掉,即讓where里不包含select。
SQL理論基礎(chǔ)是關(guān)系代數(shù),主要操作僅包括:并、差、積、選擇、投影。而一個(gè)嵌套子查詢可等價(jià)轉(zhuǎn)換成一個(gè)連接(join)操作,如:
select s_grade
from staff
where s_city not in (
select p_city
from proj
where s_empname = p_pname
)
這是個(gè)在where條件里嵌套了not in子查詢的SQL語(yǔ)句,它可以用left outer join和left semi join進(jìn)行等價(jià)轉(zhuǎn)換,示例如下,這是Panthera自動(dòng)轉(zhuǎn)換完成得到的等價(jià)SQL。這條SQL語(yǔ)句不再包含嵌套子查詢,
select panthera_10.panthera_1 as s_grade from (select panthera_1, panthera_4, panthera_6, s_empname, s_city from (select s_grade as panthera_1, s_city as panthera_4, s_empname as panthera_6, s_empname as s_empname, s_city as s_city from staff) panthera_14 left outer join (select panthera_16.panthera_7 as panthera_7, panthera_16.panthera_8 as panthera_8, panthera_16.panthera_9 as panthera_9, panthera_16.panthera_12 as panthera_12, panthera_16.panthera_13 as panthera_13 from (select panthera_0.panthera_1 as panthera_7, panthera_0.panthera_4 as panthera_8, panthera_0.panthera_6 as panthera_9, panthera_0.s_empname as panthera_12, panthera_0.s_city as panthera_13 from (select s_grade as panthera_1, s_city as panthera_4, s_empname as panthera_6, s_empname, s_city from staff) panthera_0 left semi join (select p_city as panthera_3, p_pname as panthera_5 from proj) panthera_2 on (panthera_0.panthera_4 = panthera_2.panthera_3) and (panthera_0.panthera_6 = panthera_2.panthera_5) where true) panthera_16 group by panthera_16.panthera_7, panthera_16.panthera_8, panthera_16.panthera_9, panthera_16.panthera_12, panthera_16.panthera_13) panthera_15 on ((((panthera_14.panthera_1 <=> panthera_15.panthera_7) and (panthera_14.panthera_4 <=> panthera_15.panthera_8)) and (panthera_14.panthera_6 <=> panthera_15.panthera_9)) and (panthera_14.s_empname <=> panthera_15.panthera_12)) and (panthera_14.s_city <=> panthera_15.panthera_13) where ((((panthera_15.panthera_7 is null) and (panthera_15.panthera_8 is null)) and (panthera_15.panthera_9 is null)) and (panthera_15.panthera_12 is null)) and (panthera_15.panthera_13 is null)) panthera_10 ;
通過(guò)可視化工具將上面兩條SQL的語(yǔ)法樹(shù)展示出來(lái),是這樣的。
這是原始的SQL抽象語(yǔ)法樹(shù)。
這是等價(jià)轉(zhuǎn)換后的抽象語(yǔ)法樹(shù),內(nèi)容太多被壓縮的無(wú)法看清,不過(guò)你可以感受一下(笑)。
那么,在程序設(shè)計(jì)上如何實(shí)現(xiàn)這樣復(fù)雜的語(yǔ)法轉(zhuǎn)換呢?當(dāng)時(shí)Panthera項(xiàng)目組合使用了幾種經(jīng)典的設(shè)計(jì)模式,每個(gè)語(yǔ)法點(diǎn)被封裝到一個(gè)類里去處理,每個(gè)類通常不過(guò)幾十行代碼,這樣整個(gè)程序非常簡(jiǎn)單、清爽。如果在測(cè)試過(guò)程中遇到不支持的語(yǔ)法點(diǎn),只需為這個(gè)語(yǔ)法點(diǎn)新增加一個(gè)類即可,團(tuán)隊(duì)協(xié)作與代碼維護(hù)非常容易。
使用裝飾模式的語(yǔ)法等價(jià)轉(zhuǎn)換類的構(gòu)造,Panthera每增加一種新的語(yǔ)法轉(zhuǎn)換能力,只需要開(kāi)發(fā)一個(gè)新的Transformer類,然后添加到下面的構(gòu)造函數(shù)代碼里即可。
private static SqlASTTransformer tf =
new RedundantSelectGroupItemTransformer(
new DistinctTransformer(
new GroupElementNormalizeTransformer(
new PrepareQueryInfoTransformer(
new OrderByTransformer(
new OrderByFunctionTransformer(
new MinusIntersectTransformer(
new PrepareQueryInfoTransformer(
new UnionTransformer(
new Leftsemi2LeftJoinTransformer(
new CountAsteriskPositionTransformer(
new FilterInwardTransformer(
//use leftJoin method to handle not exists for correlated
new CrossJoinTransformer(
new PrepareQueryInfoTransformer(
new SubQUnnestTransformer(
new PrepareFilterBlockTransformer(
new PrepareQueryInfoTransformer(
new TopLevelUnionTransformer(
new FilterBlockAdjustTransformer(
new PrepareFilterBlockTransformer(
new ExpandAsteriskTransformer(
new PrepareQueryInfoTransformer(
new CrossJoinTransformer(
new PrepareQueryInfoTransformer(
new ConditionStructTransformer(
new MultipleTableSelectTransformer(
new WhereConditionOptimizationTransformer(
new PrepareQueryInfoTransformer(
new InTransformer(
new TopLevelUnionTransformer(
new MinusIntersectTransformer(
new NaturalJoinTransformer(
new OrderByNotInSelectListTransformer(
new RowNumTransformer(
new BetweenTransformer(
new UsingTransformer(
new SchemaDotTableTransformer(
new NothingTransformer())))))))))))))))))))))))))))))))))))));
而在具體的Transformer類中,則使用組合模式對(duì)抽象語(yǔ)法樹(shù)AST進(jìn)行遍歷,以下為Between語(yǔ)法節(jié)點(diǎn)的遍歷。我們看到使用組合模式進(jìn)行樹(shù)的遍歷不需要用遞歸算法,因?yàn)檫f歸的特性已經(jīng)隱藏在樹(shù)的結(jié)構(gòu)里面了。
@Override
protected void transform(CommonTree tree, TranslateContext context) throws SqlXlateException {
tf.transformAST(tree, context);
trans(tree, context);
}
void trans(CommonTree tree, TranslateContext context) {
// deep firstly
for (int i = 0; i < tree.getChildCount(); i++) {
trans((CommonTree) (tree.getChild(i)), context);
}
if (tree.getType() == PantheraExpParser.SQL92_RESERVED_BETWEEN) {
transBetween(false, tree, context);
}
if (tree.getType() == PantheraExpParser.NOT_BETWEEN) {
transBetween(true, tree, context);
}
}
將等價(jià)轉(zhuǎn)換后的抽象語(yǔ)法樹(shù)AST再進(jìn)一步轉(zhuǎn)換成Hive格式的抽象語(yǔ)法樹(shù),就可以交給Hive的語(yǔ)義分析器去處理了,從而也就實(shí)現(xiàn)了對(duì)標(biāo)準(zhǔn)SQL的支持。
當(dāng)時(shí)Facebook為證明Hive對(duì)數(shù)據(jù)倉(cāng)庫(kù)的支持,手工將TPC-H的測(cè)試SQL轉(zhuǎn)換成Hive QL,將這些手工Hive QL和Panthera進(jìn)行對(duì)比測(cè)試,兩者性能各有所長(zhǎng),總體上不相上下,說(shuō)明Panthera自動(dòng)進(jìn)行語(yǔ)法分析和轉(zhuǎn)換的效率還行。
Panthera(ASE)和Facebook手工Hive QL對(duì)比測(cè)試:
標(biāo)準(zhǔn)SQL語(yǔ)法集的語(yǔ)法點(diǎn)很多,007進(jìn)行各種關(guān)系代數(shù)等價(jià)變形,也不可能適配所有標(biāo)準(zhǔn)SQL語(yǔ)法。
SQL注入
常見(jiàn)的Web攻擊手段,如下圖所示,攻擊者在HTTP請(qǐng)求中注入惡意SQL命令(drop table users;),服務(wù)器用請(qǐng)求參數(shù)構(gòu)造數(shù)據(jù)庫(kù)SQL命令時(shí),惡意SQL被一起構(gòu)造,并在數(shù)據(jù)庫(kù)中執(zhí)行。
但JDBC的PrepareStatement可阻止SQL注入攻擊,MyBatis之類的ORM框架也可以阻止SQL注入,請(qǐng)從數(shù)據(jù)庫(kù)引擎的工作機(jī)制解釋PrepareStatement和MyBatis的防注入攻擊的原理。
本文摘自 :https://blog.51cto.com/u