摘要:开发一款能支持标准数据库SQL的大数据仓库引擎,让那些在Oracle上运行良好的SQL可以直接运行在Hadoop上,而不需要重写成Hive QL。

本文分享自华为云社区《​​​​​​​​​​​​​​从零开发大数据SQL引擎》,作者:JavaEdge 。

学习大数据技术的核心原理,掌握一些高效的思考和思维方式,构建自己的技术知识体系。明白了原理,有时甚至不需要学习,顺着原理就可以推导出各种实现细节。

各种知识表象看杂乱无章,若只是学习繁杂知识点,固然自己的知识面是有限的,并且遇到问题的应变能力也很难提高。所以有些高手看起来似乎无所不知,不论谈论起什么技术,都能头头是道,其实并不是他们学习、掌握了所有技术,而是他们是在谈到这个问题时,才开始进行推导,并迅速得出结论。

高手不一定要很资深、经验丰富,把握住技术的核心本质,掌握快速分析推导的能力,能迅速将自己的知识技能推到陌生领域,就是高手。

本系列专注大数据开发需要关注的问题及解决方案。跳出繁杂知识表象,掌握核心原理和思维方式,进而融会贯通各种技术,再通过各种实践训练,成为终极高手。

大数据仓库Hive

作为一个成功的大数据仓库,它将SQL语句转换成MapReduce执行过程,并把大数据应用的门槛下降到普通数据分析师和工程师就可以很快上手的地步。

但Hive也有问题,由于它使用自定义Hive QL,对熟悉Oracle等传统数据仓库的分析师有上手难度。特别是很多企业使用传统数据仓库进行数据分析已久,沉淀大量SQL语句,非常庞大也非常复杂。某银行的一条统计报表SQL足足两张A4纸,光是完全理解可能就要花很长时间,再转化成Hive QL更费力,还不说可能引入bug。

开发一款能支持标准数据库SQL的大数据仓库引擎,让那些在Oracle上运行良好的SQL可以直接运行在Hadoop上,而不需要重写成Hive QL。

Hive处理过程

  1. 将输入的Hive QL经过语法解析器转换成Hive抽象语法树(Hive AST)
  2. 将Hive AST经过语义分析器转换成MapReduce执行计划
  3. 将生成的MapReduce执行计划和Hive执行函数代码提交到Hadoop执行

可见,最简单的,对第一步改造即可。考虑替换Hive语法解析器:能将标准SQL转换成Hive语义分析器能处理的Hive抽象语法树,即红框代替黑框。

红框内:浅蓝色是个开源的SQL语法解析器,将标准SQL解析成标准SQL抽象语法树(SQL AST),后面深蓝色定制开发的SQL抽象语法树分析与转换器,将SQL AST转换成Hive AST。

那么关键问题就来了:

标准SQL V.S Hive QL

  • 语法表达方式,Hive QL语法和标准SQL语法略有不同
  • Hive QL支持的语法元素比标准SQL要少很多,比如,数据仓库领域主要的测试集TPC-H所有的SQL语句,Hive都不支持。尤其是Hive不支持复杂嵌套子查询,而数据仓库分析中嵌套子查询几乎无处不在。如下SQL,where条件existes里包含了另一条SQL:
  1. select o_orderpriority, count(*) as order_count
  2. from orders
  3. where o_orderdate >= date '[DATE]'
  4. and o_orderdate < date '[DATE]' + interval '3' month
  5. and exists
  6. (select *
  7. from lineitem
  8. where l_orderkey = o_orderkey
  9. and l_commitdate < l_receiptdate)
  10. group by o_orderpriority
  11. order by o_orderpriority;

开发支持标准SQL语法的SQL引擎难点,就是消除复杂嵌套子查询掉,即让where里不包含select。

SQL理论基础是关系代数,主要操作仅包括:并、差、积、选择、投影。而一个嵌套子查询可等价转换成一个连接(join)操作,如:

  1. select s_grade
  2. from staff
  3. where s_city not in (
  4. select p_city
  5. from proj
  6. where s_empname = p_pname
  7. )

这是个在where条件里嵌套了not in子查询的SQL语句,它可以用left outer join和left semi join进行等价转换,示例如下,这是Panthera自动转换完成得到的等价SQL。这条SQL语句不再包含嵌套子查询,

  1. 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 ;
  1. 通过可视化工具将上面两条SQL的语法树展示出来,是这样的。

这是原始的SQL抽象语法树。

这是等价转换后的抽象语法树,内容太多被压缩的无法看清,不过你可以感受一下(笑)。

那么,在程序设计上如何实现这样复杂的语法转换呢?当时Panthera项目组合使用了几种经典的设计模式,每个语法点被封装到一个类里去处理,每个类通常不过几十行代码,这样整个程序非常简单、清爽。如果在测试过程中遇到不支持的语法点,只需为这个语法点新增加一个类即可,团队协作与代码维护非常容易。

使用装饰模式的语法等价转换类的构造,Panthera每增加一种新的语法转换能力,只需要开发一个新的Transformer类,然后添加到下面的构造函数代码里即可。

  1. private static SqlASTTransformer tf =
  2. new RedundantSelectGroupItemTransformer(
  3. new DistinctTransformer(
  4. new GroupElementNormalizeTransformer(
  5. new PrepareQueryInfoTransformer(
  6. new OrderByTransformer(
  7. new OrderByFunctionTransformer(
  8. new MinusIntersectTransformer(
  9. new PrepareQueryInfoTransformer(
  10. new UnionTransformer(
  11. new Leftsemi2LeftJoinTransformer(
  12. new CountAsteriskPositionTransformer(
  13. new FilterInwardTransformer(
  14. //use leftJoin method to handle not exists for correlated
  15. new CrossJoinTransformer(
  16. new PrepareQueryInfoTransformer(
  17. new SubQUnnestTransformer(
  18. new PrepareFilterBlockTransformer(
  19. new PrepareQueryInfoTransformer(
  20. new TopLevelUnionTransformer(
  21. new FilterBlockAdjustTransformer(
  22. new PrepareFilterBlockTransformer(
  23. new ExpandAsteriskTransformer(
  24. new PrepareQueryInfoTransformer(
  25. new CrossJoinTransformer(
  26. new PrepareQueryInfoTransformer(
  27. new ConditionStructTransformer(
  28. new MultipleTableSelectTransformer(
  29. new WhereConditionOptimizationTransformer(
  30. new PrepareQueryInfoTransformer(
  31. new InTransformer(
  32. new TopLevelUnionTransformer(
  33. new MinusIntersectTransformer(
  34. new NaturalJoinTransformer(
  35. new OrderByNotInSelectListTransformer(
  36. new RowNumTransformer(
  37. new BetweenTransformer(
  38. new UsingTransformer(
  39. new SchemaDotTableTransformer(
  40. new NothingTransformer())))))))))))))))))))))))))))))))))))));

而在具体的Transformer类中,则使用组合模式对抽象语法树AST进行遍历,以下为Between语法节点的遍历。我们看到使用组合模式进行树的遍历不需要用递归算法,因为递归的特性已经隐藏在树的结构里面了。

  1. @Override
  2. protected void transform(CommonTree tree, TranslateContext context) throws SqlXlateException {
  3. tf.transformAST(tree, context);
  4. trans(tree, context);
  5. }
  6.  
  7. void trans(CommonTree tree, TranslateContext context) {
  8. // deep firstly
  9. for (int i = 0; i < tree.getChildCount(); i++) {
  10. trans((CommonTree) (tree.getChild(i)), context);
  11. }
  12. if (tree.getType() == PantheraExpParser.SQL92_RESERVED_BETWEEN) {
  13. transBetween(false, tree, context);
  14. }
  15. if (tree.getType() == PantheraExpParser.NOT_BETWEEN) {
  16. transBetween(true, tree, context);
  17. }
  18. }

将等价转换后的抽象语法树AST再进一步转换成Hive格式的抽象语法树,就可以交给Hive的语义分析器去处理了,从而也就实现了对标准SQL的支持。

当时Facebook为证明Hive对数据仓库的支持,手工将TPC-H的测试SQL转换成Hive QL,将这些手工Hive QL和Panthera进行对比测试,两者性能各有所长,总体上不相上下,说明Panthera自动进行语法分析和转换的效率还行。

Panthera(ASE)和Facebook手工Hive QL对比测试:

标准SQL语法集的语法点很多,007进行各种关系代数等价变形,也不可能适配所有标准SQL语法。

SQL注入

常见的Web攻击手段,如下图所示,攻击者在HTTP请求中注入恶意SQL命令(drop table users;),服务器用请求参数构造数据库SQL命令时,恶意SQL被一起构造,并在数据库中执行。

但JDBC的PrepareStatement可阻止SQL注入攻击,MyBatis之类的ORM框架也可以阻止SQL注入,请从数据库引擎的工作机制解释PrepareStatement和MyBatis的防注入攻击的原理。

点击关注,第一时间了解华为云新鲜技术~

开发一个不需要重写成Hive QL的大数据SQL引擎的更多相关文章

  1. 开发一个 Windows 级别的操作系统难度有多大?

    在搜索进程相关问题的时候,无意间看到了知乎上面的这个问题,这也是困惑我的问题,只是自己比较懒,没有刨根问底,这次无意间看到了,并且认真看了大神的回答,很受启发,作为记录,贴于此,与各位分享: 来源:知 ...

  2. hive(在大数据集合上的类SQL查询和表)学习

    1.jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true&characterEncoding=UTF-8&use ...

  3. Android(java)学习笔记220:开发一个多界面的应用程序之界面间数据传递

    1.界面跳转的数据传递 (1)intent.setData() --> intent.getData():     传递的数据比较简单,一般是文本类型的数据String:倘若我们传递的数据比较复 ...

  4. Android(java)学习笔记163:开发一个多界面的应用程序之界面间数据传递

    1.界面跳转的数据传递 (1)intent.setData() --> intent.getData():     传递的数据比较简单,一般是文本类型的数据String:倘若我们传递的数据比较复 ...

  5. 开发一个代码的自动生成器,使用Jfinal4.3+Swagger+Sql

    -- 所有表名select column_name 列名, data_type 字段类型, column_comment 字段注释  from information_schema.columns  ...

  6. 不care工具,在大数据平台中Hive能自动处理SQL

    摘要:有没有更简单的办法,可以直接将SQL运行在大数据平台? 本文分享自华为云社区<Hive执行原理>,作者: JavaEdge . MapReduce简化了大数据编程的难度,使得大数据计 ...

  7. 如何使用Add-on SDK开发一个自己的火狐扩展

    黄聪:如何使用Add-on SDK开发一个自己的火狐扩展 火狐开放了扩展的开发权限给程序员们,相信很多人都会希望自己做一些扩展来方便一些使用. 我最近做一些项目也需要开发一个火狐扩展,方便收集自己需要 ...

  8. 黄聪:如何使用Add-on SDK开发一个自己的火狐扩展

    火狐开放了扩展的开发权限给程序员们,相信很多人都会希望自己做一些扩展来方便一些使用. 我最近做一些项目也需要开发一个火狐扩展,方便收集自己需要的数据,因此研究了几天怎么开发,现在已经差不多完成了,就顺 ...

  9. 大数据【五】Hive(部署;表操作;分区)

    一 概述 就像我们所了解的sql一样,Hive也是一种数据仓库,不同的是hive是在hadoop大数据生态圈中所用.这篇博客我主要介绍Hive的简单表运用. Hive是Hadoop 大数据生态圈中的数 ...

随机推荐

  1. 什么叫 CC 攻击?什么叫 DDOS 攻击?

    CC 攻击,主要是用来攻击页面的,模拟多个用户不停的对你的页面进行访问,从而使你的系统资源消耗殆尽.DDOS 攻击,中文名叫分布式拒绝服务攻击,指借助服务器技术将多个计算机联合起来作为攻击平台,来对一 ...

  2. String 是最基本的数据类型吗?

    不是. Java中基本数据类型只有8个:byte.short.int.long.float.double.char.boolean:除了基本类型(primitive type),剩下都是引用类型(re ...

  3. java-StringBuilder

    一个可变的字符序列. String类的对象内容不可以改变,所以每当进行字符串恶拼接时,总是会在内存中创建一个新的对象,所以经常改变内容的字符串 所以最好不要用String,因为每次生成的对象都会对系统 ...

  4. 学习GlusterFS(二)

    环境准备 3台机器,每个机器双网卡,每个机器还需要额外添加1个10GB的磁盘用于测试 机器系统版本是centos6.6 1 2 3 4 5 [root@gluster-1-1 ~]# uname -r ...

  5. 详解BI系统中的任务调度

    任务调度是一个通用的计算机概念,可以简单地理解为计算机基于一定时间频率,自动执行一项进程任务.任务调度是操作系统的重要组成部分,Windows系统中的定时任务和Linux的Crontab都是常用的系统 ...

  6. python办公自动化系列之金蝶K3(三)

    小爬在之前的两篇文章 [python办公自动化系列之金蝶K3自动登录(一)].[python办公自动化系列之金蝶K3自动登录(二)]带大家系统搞定了K3客户端的自动登录难题,但是搞定[自动登录]只是我 ...

  7. 【前端小技巧】利用border画三角形及梯形

    border是围绕元素内容和内边距的一条或多条线,border 属性允许你规定元素边框的样式.宽度和颜色 值: border-width 粗细 none/hidden/solid/dashed/dot ...

  8. CSS实例:翻转图片、滚动图片栏、打开大门

    CSS 翻转图片主要用到的技术除了3D翻转和定位 ,还用到了一个属性 backface-visibility:visable|hidden;该属性主要是用来设定元素背面是否可见. 效果图如下: 具体的 ...

  9. html5手机页面的那些meta

    一.普通手机页的设置1.<meta name="viewport" content=""/>说明:屏幕的缩放 content的几个属性: width ...

  10. google fonts 国内使用解决方案

    由于众所周知的原因,国内使用google font库有很大的问题. 解决方案1:使用国内镜像如360网站卫士常用前端公共库CDN服务 优点:使用方便 缺点:目标用户包含国外的开发者,不清楚国外用户的加 ...