摘要:開發(fā)一款能支持標準數(shù)據(jù)庫SQL的大數(shù)據(jù)倉庫引擎,讓那些在Oracle上運行良好的SQL可以直接運行在Hadoop上,而不需要重寫成Hive QL。
本文分享自華為云社區(qū)《???????????????從零開發(fā)大數(shù)據(jù)SQL引擎??》,作者:JavaEdge 。
學(xué)習(xí)大數(shù)據(jù)技術(shù)的核心原理,掌握一些高效的思考和思維方式,構(gòu)建自己的技術(shù)知識體系。明白了原理,有時甚至不需要學(xué)習(xí),順著原理就可以推導(dǎo)出各種實現(xiàn)細節(jié)。
各種知識表象看雜亂無章,若只是學(xué)習(xí)繁雜知識點,固然自己的知識面是有限的,并且遇到問題的應(yīng)變能力也很難提高。所以有些高手看起來似乎無所不知,不論談?wù)撈鹗裁醇夹g(shù),都能頭頭是道,其實并不是他們學(xué)習(xí)、掌握了所有技術(shù),而是他們是在談到這個問題時,才開始進行推導(dǎo),并迅速得出結(jié)論。
高手不一定要很資深、經(jīng)驗豐富,把握住技術(shù)的核心本質(zhì),掌握快速分析推導(dǎo)的能力,能迅速將自己的知識技能推到陌生領(lǐng)域,就是高手。
本系列專注大數(shù)據(jù)開發(fā)需要關(guān)注的問題及解決方案。跳出繁雜知識表象,掌握核心原理和思維方式,進而融會貫通各種技術(shù),再通過各種實踐訓(xùn)練,成為終極高手。
大數(shù)據(jù)倉庫Hive
作為一個成功的大數(shù)據(jù)倉庫,它將SQL語句轉(zhuǎn)換成MapReduce執(zhí)行過程,并把大數(shù)據(jù)應(yīng)用的門檻下降到普通數(shù)據(jù)分析師和工程師就可以很快上手的地步。
但Hive也有問題,由于它使用自定義Hive QL,對熟悉Oracle等傳統(tǒng)數(shù)據(jù)倉庫的分析師有上手難度。特別是很多企業(yè)使用傳統(tǒng)數(shù)據(jù)倉庫進行數(shù)據(jù)分析已久,沉淀大量SQL語句,非常龐大也非常復(fù)雜。某銀行的一條統(tǒng)計報表SQL足足兩張A4紙,光是完全理解可能就要花很長時間,再轉(zhuǎn)化成Hive QL更費力,還不說可能引入bug。
開發(fā)一款能支持標準數(shù)據(jù)庫SQL的大數(shù)據(jù)倉庫引擎,讓那些在Oracle上運行良好的SQL可以直接運行在Hadoop上,而不需要重寫成Hive QL。
Hive處理過程
- 將輸入的Hive QL經(jīng)過語法解析器轉(zhuǎn)換成Hive抽象語法樹(Hive AST)
- 將Hive AST經(jīng)過語義分析器轉(zhuǎn)換成MapReduce執(zhí)行計劃
- 將生成的MapReduce執(zhí)行計劃和Hive執(zhí)行函數(shù)代碼提交到Hadoop執(zhí)行
可見,最簡單的,對第一步改造即可。考慮替換Hive語法解析器:能將標準SQL轉(zhuǎn)換成Hive語義分析器能處理的Hive抽象語法樹,即紅框代替黑框。
紅框內(nèi):淺藍色是個開源的SQL語法解析器,將標準SQL解析成標準SQL抽象語法樹(SQL AST),后面深藍色定制開發(fā)的SQL抽象語法樹分析與轉(zhuǎn)換器,將SQL AST轉(zhuǎn)換成Hive AST。
那么關(guān)鍵問題就來了:
標準SQL V.S Hive QL
- 語法表達方式,Hive QL語法和標準SQL語法略有不同
- Hive QL支持的語法元素比標準SQL要少很多,比如,數(shù)據(jù)倉庫領(lǐng)域主要的測試集TPC-H所有的SQL語句,Hive都不支持。尤其是Hive不支持復(fù)雜嵌套子查詢,而數(shù)據(jù)倉庫分析中嵌套子查詢幾乎無處不在。如下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;
開發(fā)支持標準SQL語法的SQL引擎難點,就是消除復(fù)雜嵌套子查詢掉,即讓where里不包含select。
SQL理論基礎(chǔ)是關(guān)系代數(shù),主要操作僅包括:并、差、積、選擇、投影。而一個嵌套子查詢可等價轉(zhuǎn)換成一個連接(join)操作,如:
select s_grade
from staff
where s_city not in (
select p_city
from proj
where s_empname = p_pname
)
這是個在where條件里嵌套了not in子查詢的SQL語句,它可以用left outer join和left semi join進行等價轉(zhuǎn)換,示例如下,這是Panthera自動轉(zhuǎn)換完成得到的等價SQL。這條SQL語句不再包含嵌套子查詢,
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 ;
通過可視化工具將上面兩條SQL的語法樹展示出來,是這樣的。
這是原始的SQL抽象語法樹。
這是等價轉(zhuǎn)換后的抽象語法樹,內(nèi)容太多被壓縮的無法看清,不過你可以感受一下(笑)。
那么,在程序設(shè)計上如何實現(xiàn)這樣復(fù)雜的語法轉(zhuǎn)換呢?當時Panthera項目組合使用了幾種經(jīng)典的設(shè)計模式,每個語法點被封裝到一個類里去處理,每個類通常不過幾十行代碼,這樣整個程序非常簡單、清爽。如果在測試過程中遇到不支持的語法點,只需為這個語法點新增加一個類即可,團隊協(xié)作與代碼維護非常容易。
使用裝飾模式的語法等價轉(zhuǎn)換類的構(gòu)造,Panthera每增加一種新的語法轉(zhuǎn)換能力,只需要開發(fā)一個新的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類中,則使用組合模式對抽象語法樹AST進行遍歷,以下為Between語法節(jié)點的遍歷。我們看到使用組合模式進行樹的遍歷不需要用遞歸算法,因為遞歸的特性已經(jīng)隱藏在樹的結(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);
}
}
將等價轉(zhuǎn)換后的抽象語法樹AST再進一步轉(zhuǎn)換成Hive格式的抽象語法樹,就可以交給Hive的語義分析器去處理了,從而也就實現(xiàn)了對標準SQL的支持。
當時Facebook為證明Hive對數(shù)據(jù)倉庫的支持,手工將TPC-H的測試SQL轉(zhuǎn)換成Hive QL,將這些手工Hive QL和Panthera進行對比測試,兩者性能各有所長,總體上不相上下,說明Panthera自動進行語法分析和轉(zhuǎn)換的效率還行。
Panthera(ASE)和Facebook手工Hive QL對比測試:
標準SQL語法集的語法點很多,007進行各種關(guān)系代數(shù)等價變形,也不可能適配所有標準SQL語法。
SQL注入
常見的Web攻擊手段,如下圖所示,攻擊者在HTTP請求中注入惡意SQL命令(drop table users;),服務(wù)器用請求參數(shù)構(gòu)造數(shù)據(jù)庫SQL命令時,惡意SQL被一起構(gòu)造,并在數(shù)據(jù)庫中執(zhí)行。
但JDBC的PrepareStatement可阻止SQL注入攻擊,MyBatis之類的ORM框架也可以阻止SQL注入,請從數(shù)據(jù)庫引擎的工作機制解釋PrepareStatement和MyBatis的防注入攻擊的原理。
本文摘自 :https://blog.51cto.com/u