前言

在实时计算中,通常是从队列中收集原始数据,这种原始数据在内存中通常是一个java bean,把数据收集过来以后,通常会把数据落地到数据库,供后面的ETL使用。举个一个简单的例子,对一个游戏来说,为了统计某个游戏,某个服务器的登陆注册

等事件,原始数据对应的java bean可能会是这样:

public class Event {
private String userName;
private String game;
private String server;
private String event;
}

Event

当数据量过大的时候,通常没有办法实时的去做一个些统计操作,例如统计按照游戏和服务器分组统计出登陆的人次是多少,对应的SQL大致如下:

select count(user_name) from event group by name,sever where event = 'login'

当有一个sql执行引擎,可以在内存中对于一批收集过来的数据执行sql计算的时候,无疑能够实时的计算出结果,另外由于sql是实时输入的,程序也可以比较灵活。

例如,收集过来的一批数据,可以换成成一个List<Map<String,Object>>形式的数据结构,通过sql执行引擎,执行某个特定的sql,得到结果(也是一个List<Map<String,Object>>形式的数据结构),demo如下

----------------
[username:user1,game:lol,server:s1,event:login]
[username:user2,game:dota2,server:s2,event:register]
[username:user3,game:lol,server:s2,event:login]
[username:user4,game:dota2,server:s3,event:register]
[username:user5,game:lol,server:s10,event:login]
[username:user6,game:dota2,server:s1,event:login]
[username:user7,game:lol,server:s1,event:login]
[username:user8,game:lol,server:s1,event:login]
[username:user9,game:lol,server:s1,event:login]
----------------
select count(*) as loginNum, game,server from event group by game,server where event='login'
----------------
[loginNum:1,game:lol,server:s2]
[loginNum:4,game:lol,server:s1]
[loginNum:1,game:lol,server:s10]
[loginNum:1,game:dota2,server:s1]
----------------

解析

此sql执行引擎只支持的sql语法中的一个很小的子集,所以我更加偏向称其为sql-like DSL(Domain Specific Language-特定领域语言),关于DSL的论述很多,我推荐两本书,一本是Martin大叔的Domain Specific Language,另外一个本是DSL in

action。之所以选择scala来实现,是因为scala语言中内置了对DSL的支持,可以很方便的实现一个自己的Parser,通过此Parser,可以解析你的DSL脚本(此处就是sql语句),得到你想要的中间结果,通常我们将中间结果称为AST(Abstract syntax tree),类似于

select {...} from {...} group by {...}  where {...}order by{...} limit {...}形式的sql语句,我将它转化成如下类型的AST。

解析器的入口为

def select: Parser[SelectStmt] = "select" ~> projectionStatements ~ fromStatements ~ opt(groupStatements) ~ opt(whereExpr) ~ opt(orderByExpr) ~ opt(limit) ~ opt(";") ^^ {
case p ~ f ~ g ~ w ~ o ~ l ~ end => SelectStmt(p, f, w, g, o, l)
}

其中,fromStatements,groupStatements,whereExpr等有是一个单独的解析器,通过scala中已经提供的parser combinators(解析器组合子),例如(~>,~,opt()...)等,将单独的解析器组合起来,可以得到更复杂的解析器,类似于lego积木,你编写一个解析

器,parserA, 只能解析某段特殊的文本,这个段文本的模式我们用patternA来表示。通过组合子 rep1sep(“,”,parserA),你就得到了一个新的解析器,这个解析器能解析的partern = patternA[,patternA][,patternA][,patternA]...

例如sql语句中的group by子句,不考虑having语法的话,大致格式是这样的 group by [tableName.]coulumn1,[tableName.]coulumn1,[tableName.]coulumn1 可见[tableName.]coulumn1这种格式的文本,可以是基本的pattern,于是可以写出一个解析器来解析这种格式的文本:

def selectIdent: Parser[SqlProj] = {
ident ~ opt("." ~> ident) ^^ {
case table ~ Some(b: String) => FieldIdent(Option(table), b)
case column ~ None => FieldIdent(None, column)
}
}

这个函数中ident值得的标示符,opt()表示的是可以有也可以没有,那么这个解析器解析的文本就可以有如下形式:标示符.标示符|标示符,那么通过rep1sep的组合子就能得到解析group by字句的解析器:

def groupStatements: Parser[SqlGroupBy] = "group" ~> "by" ~> rep1sep(selectIdent, ",") ^^ {
case keys => SqlGroupBy(keys)
}

其他部分的sql字句的解析大抵如此,整个项目的代码,在github上。下一篇讲拿到AST之后,怎么执行,得到想要的结果。

用scala实现一个sql执行引擎-(上)的更多相关文章

  1. 用scala实现一个sql执行引擎-(下)

    执行 上一篇讲述了如何通过scala提供的内置DSL支持,实现一个可以解析sql的解析器,这篇讲如何拿到了解析结果-AST以后,如何在数据上进行操作,得到我们想要的结果.之前说到,为什么选择scala ...

  2. 自己实现一个SQL解析引擎

    自己实现一个SQL解析引擎 功能:将用户输入的SQL语句序列转换为一个可运行的操作序列,并返回查询的结果集. SQL的解析引擎包含查询编译与查询优化和查询的执行,主要包含3个步骤: 查询分析: 制定逻 ...

  3. 自己动手写SQL执行引擎

    自己动手写SQL执行引擎 前言 在阅读了大量关于数据库的资料后,笔者情不自禁产生了一个造数据库轮子的想法.来验证一下自己对于数据库底层原理的掌握是否牢靠.在笔者的github中给这个database起 ...

  4. spark sql 执行计划生成案例

    前言 一个SQL从词法解析.语法解析.逻辑执行计划.物理执行计划最终转换为可以执行的RDD,中间经历了很多的步骤和流程.其中词法分析和语法分析均有ANTLR4完成,可以进一步学习ANTLR4的相关知识 ...

  5. 给隔壁的妹子讲『一个SQL语句是如何执行的?』

    前言 SQL作为Web开发是永远离开不的一个话题,天天写SQL,可是你知道一个SQL是如何执行的吗? select name from user where id = 1; 上面是一个简单的查询语句, ...

  6. Oracle数据库该如何着手优化一个SQL

    这是个终极问题,因为优化本身的复杂性实在是难以总结的,很多时候优化的方法并不是用到了什么高深莫测的技术,而只是一个思想意识层面的差异,而这些都很可能连带导致性能表现上的巨大差异. 所以有时候我们应该先 ...

  7. SQL执行过程中的性能负载点

    一.SQL执行过程 1.用户连接数据库,执行SQL语句: 2.先在内存进行内存读,找到了所需数据就直接交给用户工作空间: 3.内存读失败,也就说在内存中没找到支持SQL所需数据,就进行物理读,也就是到 ...

  8. Farseer.net轻量级开源框架 中级篇:SQL执行报告

    导航 目   录:Farseer.net轻量级开源框架 目录 上一篇:Farseer.net轻量级开源框架 中级篇: 数据库切换 下一篇:Farseer.net轻量级开源框架 中级篇: 探究ORM(M ...

  9. scrapy 源码解析 (三):启动流程源码分析(三) ExecutionEngine执行引擎

    ExecutionEngine执行引擎 上一篇分析了CrawlerProcess和Crawler对象的建立过程,在最终调用CrawlerProcess.start()之前,会首先建立Execution ...

随机推荐

  1. Django model 中meta options之 abstract

    当abstract=True时,这个model就变成了abstrct base class,那这个基类有什么特性呢? 当某一model中设置了abstract=True时,就会使该model中的字段都 ...

  2. Zepto源码

    // Zepto.js // (c) 2010-2016 Thomas Fuchs // Zepto.js may be freely distributed under the MIT licens ...

  3. python中getattr函数 hasattr函数

    hasattr(object, name)作用:判断对象object是否包含名为name的特性(hasattr是通过调用getattr(ojbect, name)是否抛出异常来实现的).示例: > ...

  4. 【SSM 3】Mybatis应用,和Hibernate的区别

    PS:每次写概念性的总结,都是各种复制,各种粘,然后各种理解各种猜.但是这一步的总结,决定了我能够再这条路上走的远近和是否开心.是否创造!so,开启Ctrl A+Ctrl C的模式吧. 接触到这个概念 ...

  5. Libevent 定时器

    先摘一点网上的介绍 libevent是一个事件触发的网络库,适用于windows.linux.bsd等多种平台,内部使用select.epoll.kqueue等系统调用管理事件机制.著名分布式缓存软件 ...

  6. 关于@property()的那些属性及ARC简介

    @property()常用的属性有:nonatomic,atomic,assign,retain,strong,weak,copy. 其中atomic和nonatomic用来决定编译器生成的gette ...

  7. Hibernate day04笔记

    整合log4j(了解) slf4j 核心jar : slf4j-api-1.6.1.jar .slf4j是日志框架,将其他优秀的日志第三方进行整合.      整合导入jar包     log4j 核 ...

  8. java三大框架之一hibernate使用入门

    综述:Hibernate的作用就是让实体类与数据库映射,使数据持久化,用于替代JDBC,使我们不致于写那么多sql语句代码. 1. 首先在官网www.hibernate.org下载hibernate包 ...

  9. FireDAC 超时

    FireDAC 超时 Timeout  expired 在Win10 正常. 在Win7 CB的DLL 正常,Delphi的DLL怎么会超时呢??? 果然是连接字符串错了.改为正确的就连接正常了!

  10. Devexpress VCL Build v2015 vol 15.2 开始测试

    增加了几个小玩意,与大版本变化根本无法匹配. 具体可以官网了解 https://www.devexpress.com/Subscriptions/New-2015.xml?product=vcl