SQL 查询优化器浅析(课前)

253 阅读22分钟

这是我参与「第四届青训营 」笔记创作活动的的第0天

大数据体系和SQL

火山引擎

例子来源:大规模机器学习平台架构设计

计算

计算方面体现在高性能计算,需要考虑的因素

  • 新硬件:CPU,GPU,多种类型网卡
  • 虚拟化产生损耗:网络和容器会进行一定的虚拟化,存储的分层池化也会带来负载均衡的问题
  • 繁多的分布式训练框架:由于机器学习平台的用户很多,并且不同任务依赖的分布式训练框架也不同(数据并行的框架,模型并行的框架,HPC框架,其他框架),不同训练框架有各自的调度和资源要求,给底层基础设施带来很多挑战

存储

对于任何一个系统,存储都是必不可少的,而对于机器学习来说,面临的挑战也有很多

  • 高性能和扩展性:随着硬件的计算性能越来越好,为满足读数据的高吞吐量,对于存储的要求非常高,比如需要面对达上百Gb/s的单租户带宽吞吐以及很小的延迟,存储的容量也是高达PB级别。为了提升模型训练的效率,需要数千个计算实例能同时访问的高性能共享存储。
  • 易用性和安全性:对于用户而言,最重要的就是简单方便,能够传输通畅,数据上下云,部分数据对安全性也有要求,所以需要隔离存储;在使用框架的时候为了能够读写存储能够像读本地文件一样方便,就需要存储接口友好,代码零修改,兼容POSIX
  • 成本:尽可能低的成本

调度

基于高性能的硬件,调度首先需要对资源(计算+存储)进行池化,火山引擎机器学习平台有一个大的计算池,里面有大量的GPU和CPU,在保证不同用户计算容器间的隔离前提下,不同客户共享整个资源池,从而提高集群的利用率。

机器学习的调度需求比较复杂。比如一次分布式训练,有 Worker、Server 和 Scheduler 角色的实例。在调度时,它需要 Gang 调度的能力,所有实例(或其中某一种角色的实例)要么都起来,要么都不起来。同时在训练过程中还需要网络的亲和性。例如同一个分布式训练的容器,申请到的资源能在一台机器肯定是最好。申请多台机器时,这些机器之间的网络连接肯定是越近越好。所以在调度上我们有一些相应的调度策略,包括 多队列调度(排队、抢占)、Gang 调度、堆叠调度等。

应用

分布式训练,加速方式主要从计算,通信,显存三个角度考虑

  • 计算:因为训练一般都用GPU,火山引擎有一个高性能算子库,自主研发了很多中细粒度高性能算子,它们的性能往往较于好的开源实现有非常明显的提升
  • 通信:火山开通了BytePS通信框架,同时利用了CPU和GPU两种异构资源来加速通信,在对拓扑的探测上做了细致的智能优化,并且支持异步和同步两种训练模式
  • 显存:主要针对超大模型场景,开源了veGiantModel,支持混合并行的策略,包括数据并行,Tensor 并行和流水线并行;可根据参数量、计算量自动切分流水线。veGiantModel 的底层是基于 BytePS 做加速的。

批式计算和流式计算

流式计算批式计算
特性对数据流进行处理,实时计算统一收集数据,存储到数据库中,然后对数据进行批量处理
时效性实时计算,低延迟非实时,高延迟
数据特征数据一般是动态的,无边界的数据一般是静态数据
应用场景实时场景,时效性高,比如实时推荐,业务监控时效性不用很高,离线计算,数据分析,离线报表
运行方式流式计算的任务是持续进行的批量计算一次性完成

这个就好比分组加密和流密码,分组加密就是批式计算,等着块生成了统一处理,而流密码则实时处理。

交互式分析引擎

背景

在开源大数据领域,交互式引擎是后来才出现的,最初,大数据领域数据处理引擎以MapReduce为主,但是MapReduce引擎采用了批处理设计理念,数据处理性能不行

  • IO密集型:Map阶段中间结果写磁盘,Reduce阶段写HDFS,多个MapReduce作业之间通过一个共享存储系统HDFS交换数据
  • 任务调度和启动开销大:大量任务需要分布式调度到各个节点上,且每个任务需要启动一个Java虚拟机运行
  • 无法充分利用内存:MapReduce是十多年前提出的分布式技术,当时内存的价格很高,所以设计理念是充分利用磁盘,而如今不再如此,新型计算引擎可以尝试通过内存加速
  • Map端和Reduce端均需要排序:这是其本身设计理念决定的,使得其无法很好地应对交互式处理场景

为了克服MapReduce地性能缺陷,Google提出了新型交互式计算引擎Dremel,它构建于Googel地GFS(Google File System)等系统之上,支撑了Google的数据分析服务BigQuery等诸多服务。Dremel的技术亮点主要有两个:一个是采用了MPP架构,使用了多层查询树,使得任务可以在数千个节点上并行窒息感和聚合结果;二是实现了嵌套数据的列式存储 ,避免读取不必要的数据,大大减少网络和磁盘IO。

分类

交互式计算引擎是具备交互式分析能力的分布式大数据计算引擎,它常用于OLAP场景。OLAP有很多实现方法,根据存储数据的方式不同可以分为ROLAP,MOLAP,HOLAP等。

  • ROLAP:基于关系型数据库的OLAP实现(Relational OLAP)。它以关系型数据库为核心,以关系型结构进行多维度的表示和存储。它将多维结构划分为两类表:一类是事实表,用来存储数据和纬度关键字;另一类是纬度表,即对每个纬度至少使用一个表来存放纬度层次,成员类别等纬度描述信息。ROLAP的最大好处是可以实时的从源数据中获取最新数据更新,以保持数据实施性,缺点在于运算效率比较低,用户等待响应时间比较长。
  • MOLAD:基于多维度的OLAP实现(Multidimensional OLAP)。它以多位数据组织方式为核心,使用多纬数据存储数据。多维数组在存储系统中形成数据立方体(Cube)的结构,此结构是经过高度优化的,可以最大程度地提高查询能力。MOLAP的优势在于借助数据多纬预处理显著提高运算效率,主要额缺陷在于占用存储空间和数据更新有一定延滞。
  • HOLAP:基于混合组织的OLAP实现(Hybrid OLAP),用户可以根据自己的业务需求,选择哪些模型采用ROLAP,哪些采用MOLAP。一般来说,将不常用或需要灵活定位的分析使用ROLAP方式,而常用,常规模型采用MOLAP实现。

Impala和Presto可用于ROLAP场景,而Druid和Kylin常用于MOLAP场景。也有人将Druid规划到“HOLAP”范畴,因为它不会进行预计算,因此是一种“ROLAP”,但同时它此用了列式存储,且为非关系型模型,因此也是一种“MOLAP”。

常见开源实现

在大数据生态圈中,主流的应用于ROLAP场景的交互式计算引擎包括Impala和Prosto,它们的特点如下:

  • Hadoop native(跟Hadoop生态系统有完好的结合)
    • 可直接在Hive Metastore对接,处理Hive中的表
    • 可直接处理存储在HDFS和HBase中的数据
  • 计算与存储分析:它们仅仅是查询引擎,不提供数据存储服务,所有要处理的数据都存储在第三方系统中,比如Hive,HDFS和HBase等
  • MPP架构:采用经典的MPP架构,具有较好的扩展性,能够对应TB甚至PB级别数据的交互式查询需求
  • 嵌套式数据存储:支持常见的列式存储格式,比如ORC(仅Presto支持)和Parquet(Impala和Presto均支持)

主流的应用于MOLAP场景的交互式计算引擎包括Druid和Kylin,它们的特点如下:

  • 数据建模:将数据分为纬度和度量两类,且所有查询必须针对以上两类列进行
  • 数据预计算:为了提高数据查询效率,MOLAP引擎一般会根据纬度和度量列,预先生成计算结果

YARN

名词解释Apache Yarn(Yet Another Resource Negotiator的缩写)是 hadoop 集群资源管理器系统,Yarn 从 hadoop 2 引入,最初是为了改善 MapReduce 的实现,但是它具有通用性,同样执行其他分布式计算模式。
职责资源调度和任务管理
组件RM(ResourceManger),NodeManager(NM),ApplicationMaster(AM),Container(容器)

背景

既然说最初是为了改善MapReduce的实现,那么有何需要改善的?MapReduce1中,有如下局限性:

  • 扩展性差:jobtracker 兼顾资源管理作业控制跟踪功能跟踪任务,启动失败或迟缓的任务,记录任务的执行状态,维护计数器),压力大,成为系统的瓶颈
  • 可靠性差:采用了 master/slave 结构,master 容易单点故障
  • 资源利用率低:基于槽位的资源分配模型,槽位是一种粗粒度的资源划分单位,通常一个任务不会用完一个槽位的资源hadoop1 分为 map slot 和 reduce slot,而它们之间资源不共享,造成一些资源空闲
  • 不支持多:不支持多种计算框架并行。

YARN 很好解决了 MapReduce1 中的局限性:yarn 基本思想:一个全局的资源管理器ResourceManager和与每个应用对应的ApplicationMaster,Resourcemanager 和 NodeManager 组成全新的通用系统,以分布式的方式管理应用程序。

组件

ResourceManager

  • 处理客户端请求
  • 启动/监控ApplicationMaster
  • 监控NodeManager
  • 资源分配与调度

APPlicationMaster

  • 程序切分
  • 为应用程序申请资源,并分配任务
  • 任务监控与容错

NodeManager

  • 单个节点上资源管理
  • 处理来自ResourceManager的命令
  • 处理来自ApplicationMaster的命令

Container 对任务运行环境的抽象,封装了CPU、内存等多维资源以及环境变量、启动命令等任务运行相关信息

Kubernetes

kubernetes,简称K8s。是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署,规划,更新,维护的一种机制。当部署完kubernetes,便拥有了一个完整的集群。

特点

  • 可移植: 支持公有云,私有云,混合云,多重云(multi-cloud)
  • 可扩展: 模块化,插件化,可挂载,可组合
  • 自动化: 自动部署,自动重启,自动复制,自动伸缩/扩展

组件

一个kubernetes集群是由一组node机器组成,这些node上会运行由kubernetes所管理的容器化应用,每个集群至少有一个工作节点。

工作节点会托管所谓的 Pods,而 Pod 就是作为应用负载的组件。 控制平面管理集群中的工作节点和 Pods。 为集群提供故障转移和高可用性, 这些控制平面一般跨多主机运行,而集群也会跨多个节点运行。

关系代数

就说一下join吧

例如,现在有一个任务让你查询某同学借阅的图书书名及作者,假设在数据库中有book表和borrow表,要查询图书的书名和作者在book表中,但是查找的人是这名同学,所以条件是借阅人为该同学,有关信息在borrow表中,因此待查询的对象为book表和borrow表,查询结果是部分属性列,我们想用选择和投影运算可以解决这个问题,可是这两个关系运算作用的对象应当为一个关系,为了解决这个问题,就可以用笛卡尔积使多个关系合并成一个关系;而连接运算就是在笛卡尔积运算的基础上进行某些选择的结果,根据定义,从两个关系的笛卡尔积中选取属性间满足一定条件的元组;大致分为

  • 一般连接
  • 等值连接
  • 自然连接

一般连接

假设两个关系RSAR中的属性组,BS中的属性组,这两个属性列数相同,而且取值是可以比较的。 如果有需要计算RS的一般连接,连接条件是C<D,首先计算两个关系的笛卡尔积,R关系的每一个元组和S关系的每一个元组串接得到新关系520行,在此基础上进行筛选,提取2、3、4、8行即为最终结果。 分析有以下要点:

  • 两个关系参与
  • 计算笛卡尔积
  • 比较两个关系中的属性组
  • 找出属性间值的比较符合条件的元组

等值连接

等值连接运算从RS的广义笛卡尔积中选择R关系在A属性组上的值等于S关系在B属性上值的元组,需要比较的通常是两个属性列是否相等,即θ= 对于前面的例子,如果将条件改为C=D,也就是查询关系RC上和关系SD上元素相等的元组进行连接,发现 其要点与一般连接不同的是需要选择属性组间值相等的元组。

在等值连接中有一种特殊情况,也可以比较两关系间某列相同元素的元组,比如

自然连接

在上一个例子中,可以发现属性列值重复存储,这样没有意义,可以去掉重复的一列,保留一列B 可以不用注明属性属于哪一个关系,连接运算符下面不用注明选择的条件,这种特殊情况的等值连接被称为自然连接,故给出定义,从RS的广义笛卡尔积上选择R关系和S关系同名属性B上值相等的元组

等值连接自然连接
保留重复的属性列需要把重复的属性列去掉
从行的角度进行运算同时从行和列的角度进行运算
相同的分量不一定能是相同的属性名相同的分量要求必须是相同的属性名

外连接

在自然连接中,有时候左右两边部分元组的属性无法匹配,合成后直接被删除了,我们将它们称为悬浮元组,而对于外连接来说,相当于对于自然连接的一种扩充,分为左、右、全外连接。自然连接中丢弃的悬浮元组在外连接中会根据左右情况予以保留。

例如 学生表(左)

学号姓名班级
1张三1
2李四1
3王五2

成绩表(右)

学号成绩班级
1901
21001

左外连接

学号姓名成绩班级
1张三901
2李四1001
3王五NULL2

右外连接

学号姓名成绩班级
1张三901
2李四1001

编译原理相关知识

词法分析

词法分析器在程序编译过程中所处的位置 词法分析器在程序编译过程中所处的位置

Lexical Analyzer,输入源程序,扫描器Scanner从左至右逐个字符地对源程序进行扫描,产生多个单词符号;单词符号的种类有

  • 基本字(关键字):beginrepeatfor等程序语言定义好的
  • 标识符:用来表示各种名字,如变量名、数组名和过程名
  • 常数:各种类型的常数
  • 运算符:+、-、*、/
  • 界符:,、;、()、space符

输出的单词符号的表示形式为(单词种别,单词自身的值),单词种别通常用整数编码表示

  • 若一个种别只有一个单词符号,则种别编码就代表该单词符号,假定基本字、运算符、界符都是一符一种。
  • 若一个种别有多个单词符号,则对于每个单词符号,给出种别编码和自身的值
  • 标识符单列一种,标识符自身的值表示成按机器字节划分的内部码。
  • 常数按类型分种,常数的值则表示成标准的二进制形式

如何设计

扫描缓冲区

两个指针分别指向起点和搜索位置,考虑到单词长度超过缓冲区的长度,造成存储不连续,可以分为两个半区互补使用。 如果某单词的结尾在一个半区找不到,那么在下一个半区一定能找得到,所以半区的长度就是程序语言允许的单词最大长度,比如某编程语言的标识符长度不超过128,就可以推断出编译器的扫描缓冲区总长度为256

状态转换图

状态转换图是一张有限方向图,节点代表状态,用圆圈表示,状态之间用箭弧连接,上面的标记(字符)代表射出的结点状态下可能出现的输入字符或字符类,一张转换图只包含有限个状态,其中有一个为初态,至少有一个终态。 状态转换图可用于识别一定的字符串,若存在一条从初态到某一终态的道路,且这条路上所有弧上的标记符连接成的字等于α,则称α被该状态转换图所识别。 终态上的*代表最后一个输入的字符不属于刚才读入的单词,将其退回去。

几点限制,不必使用超前搜索

  • 所有基本字都是保留字,用户不能用它们作自己的标识符
  • 基本字作为特殊的标识符来处理,使用保留字表
  • 如果基本字、标识符和常数(或括号)之间没有确定的运算符或界符作为间隔,则必须使用一个空白符作间隔

语法分析

语法分析正是建立在词法分析的基础上,以词法分析识别出的正确单词符号串为输入,判断是否符合相应的语法规则,同时进行错误处理,为语义分析和后续步骤做准备。在编译的过程中处于核心地位。执行语法分析的程序称为语法分析程序,也称为语法分析器

那么何为文法?

以前上编译原理课,经常说自上而下的语法分析, 文法是描述语言的语法结构的形式规则(即语法规则),这些规则必须准确且可理解。文法通过对高级语言的语法规则进行形式化的描述,从而能够更加精确的描述高级语言程序的语法结构,适合描述高级语言语法规则的文法是上下文无关文法。

选用哪条文法规则?

比如已知文法

E → T|E+T|E-T
TF|T*F|T/F
F(E)|i
让你给出下列表达式的最左最右推导
i*(i+i)
i+i*i

思想 1.输入待推导的表达式 2.对表达式字符串进行第一行文法判定

  • 判断是否有+ or -运算符括号内除外,是则输出E→E+T or E→E-T+之前的所有字符返回进行2+之后的字符进行3;否则进行下一步。
  • 输出E→T,所有字符串进行3

3.对表达式字符串进行第二行文法判定

  • 判断是否有* or /运算符括号内除外,是则输出E→E*T or E→E/T+之前的所有字符返回进行2+之后的字符进行3;否则进行下一步。
  • 输出T→F,所有字符串进行4

4.对表达式字符串进行第三行文法判定 第一个字符是否为(,是则输出F→(E),然后将该字符串的()删除,返回2;否则输出F →i,操作结束。

针对LL1文法

比如对于自上而下的语法分析来说,无论是递归下降子程序法还是非递归预测分析法,他们都只能处理LL1文法

根据LL1文法的三个条件

  • 需要消除左递归包括间接左递归
  • 需要消除回溯
  • first集合和follow集合不相交

所以首先需要重写文法,消除左递归,避免陷入死循环,提取左因子,避免回溯。

我们知道,直接消除左递归的一般形式为

p->pα|β

p->βp'
p'->αp'|ε

消除间接左递归 对于给定文法G(S)

S->Qc|c
Q->Rb|b
R->Sa|a

对于此文法,不存在直接左递归,但是S、Q、R都是间接左递归的。给出一个文法消除左递归的条件

  • 不含以ε为右部的产生式
  • 不含回路

满足这两个条件比较容易,接下来的思路就是将间接转化为直接,层层带入,转化为只关于S的闭包。 比如,把文法G中的所有非终结符按任一种顺序排列P1、 P2、...Pn,按此顺序执行: 从上而下考察每一个产生式Pi(i∈[i,n]),将之前的非终结符号Pj(j∈[1,i-1])带入Pi(如果可以带入);然后可以得到Pi的一个产生式,如果存在直接左递归,则消除,否则,开始新的一轮循环,考察Pi+1

for(从1到n的每个i){
	for(从1到i-1的每个j){
		把形如Pi->Pγ的产生式改成Pi->δ1γ|δkγ|δ2γ|...|δkγ,其中Pj->δ1|δk|δ2|...|δk是关于Aj的所有产生式
	}
	消除关于Pi产生式的直接左递归
}
S->Sabc|abc|bc|c
Q->Sab|ab|b
R->Sa|a

消除回溯

为了消除回溯必须保证,对文法的任何非终结符,当要它去匹配输入串时,能够根据它所面临的输入符号准确地指派它的一个候选去执行任务,并且此候选的工作结果应该是确信无疑的。

FIRST集合假设G是一个不含左递归的文法,对G的所有非终结符的每个候选α定义它的终结首符集FIRST(α)为:FIRST(α)={a|α=*=>a...,a∈Vt},特别的,若α=*=>ε,则规定ε∈FIRST(α)

如果非终结符A的所有候选首符集两两不相交,即A的任何两个不同候选αiαjFIRST(αi)∩FIRST(αj)=Ø;当要求A匹配输入串时,A能根据它面临的第一个输入符号a,准确地指派一个候选去执行任务,这个候选就是那个终结首符集含有a的候选式α,它是唯一的,因为任何候选首符集两两不相交。

这样就可以根据当前的终结符找一个唯一可能的候选,可是一个文法可能最开始的时候并不具备一个非终结符的多个候选首符集两两不相交这个条件,可以提取公共左因子来进行改造,来使它具备这个条件。

经过反复提取左因子,就能把每个非终结符(包括新引进者)的所有候选首符集变得两两不相交。

FOLLOW集合:假定S是文法G的开始符号,对于G的任何非终结符A,我们定义FOLLOW集合 FOLLOW(A)={a|S=*=>...Aa...,a∈Vt},也就是FOLLOW(A)集合是所有紧跟A之后的终结符或#所组成的集合(#是句尾的标志),称FOLLOW(A)A的随符集。

计算所有非终结符号AFOLLOW(A)集合时,不断应用下面的规则,直到再没有新的终结符号可以被加入到任意的FOLLOW集合中为止。 注意:当A是最右部的时候,将#加入到FOLLOW(A)

  • #放到FOLLOW(A)中,A是文法的开始符号。
  • 如果存在一个产生式A→αBβ,那么FIRST(B)中除ε之外的所有符号都在FOLLOW(B)中。FOLLOW(B)是求跟在B后的终结符或#组成的集合,因此对于跟在B后的β,它的FIRST集合就是FOLLOW(B)的子集。
  • 如果存在一个产生式A→αB,或存在产生式A→αBβFIRST(B)包含ε,那么FOLLOW(A)中的所有符号都在FOLLOW(B)中。对于A→αBβ,且β多步推导出ε,那么可以用αB替换AB后面紧跟的字符就是A后面紧跟的字符。

详情请见:消除回溯

抽象语法树

当在开发语言时,可能在开始的时候,选择LL1文法来描述语言的语法规则,编译器前端生成LL1语法树,编译器后端对LL1语法树进行处理,生成字节码或者是汇编代码。但是随着工程的开发,在语言中加入了更多的特性,用LL1文法描述时,感觉限制很大,并且编写文法时很吃力,所以这个时候决定采用LR1文法来描述语言的语法规则,把编译器前端改生成LR1语法树,但在这个时候,你会发现很糟糕,因为以前编译器后端是对LL1语树进行处理,不得不同时也修改后端的代码。

抽象语法树的第一个特点为:不依赖于具体的文法。无论是LL1文法,还是LR1,或者还是其它的方法,都要求在语法分析时候,构造出相同的语法树,这样可以给编译器后端提供了清晰,统一的接口。即使是前端采用了不同的文法,都只需要改变前端代码,而不用连累到后端。即减少了工作量,也提高的编译器的可维护性。

抽象语法树的第二个特点为:不依赖于语言的细节。在编译器家族中,大名鼎鼎的gcc算得上是一个老大哥了,它可以编译多种语言,例如c,c++,java,ADA,Object C, FORTRAN, PASCAL, COBOL等等。在前端gcc对不同的语言进行词法,语法分析和语义分析后,产生抽象语法树形成中间代码作为输出,供后端处理。要做到这一点,就必须在构造语法树时,不依赖于语言的细节,例如在不同的语言中,类似于if-condition-then这样的语句有不同的表示方法。

其流程即是词法分析和语法分析,这两步是从代码生成抽象语法树的关键所在

  • 词法分析,也叫扫描scanner 它读取我们的代码,然后把它们按照预定的规则合并成一个个的标识 tokens。同时,它会移除空白符、注释等。最后,整个代码将被分割进一个 tokens 列表(或者说一维数组)。当词法分析源代码的时候,它会一个一个字母地读取代码,所以很形象地称之为扫描 - scans。当它遇到空格、操作符,或者特殊符号的时候,它会认为一个话已经完成了。
  • 语法分析,也称解析器 它会将词法分析出来的数组转换成树形的形式,同时,验证语法。语法如果有错的话,抛出语法错误。当生成树的时候,解析器会删除一些没必要的标识 tokens(比如:不完整的括号),因此 AST 不是 100% 与源码匹配的。

分布式系统中shuffle的实现方式

MapReduce的流程,执行过程分为三个环节

  • map 阶段负责读取和解析数据,
  • shuffle 阶段负责把对应的数据分发给相应的 reducer
  • reduce 阶段则做汇总属于自己的数据并做最终业务逻辑处理。

大量IO导致MR性能不佳,如何优化IO,我们需要关注shuffle阶段,整个shuffle阶段可以拆成两部分shuffle writeshuffle read

  • shuffle write:mapper把处理好的数据写到本地磁盘上,一般会以reduce好处理的形式组织
  • shuffle read:reducer把分散在各个mapper的数据读取到本地并合并

由于shuffle read在shuffle write之后,相对被动,上游写了多少文件,怎么写的,下游就只能相应去处理,所以重点关注shuffle write,shuffle的关键在于为每个下游独立输出数据,也就是把每个reducer的数据写在一起,能想到每个mapper都为每个reducer生成一个文件,再把对应数据都写进去,这就是hash shuffle,分为两种机制,普通运行机制合并运行机制,合并机制主要是通过复用buffer来优化shuffle过程中产生的小文件的数量,hash shuffle不具有排序能力

详见:shuffle机制和原理分析

group-by与join的执行方式

MySQL中,group by用来分组,根据一个或多个列对结果集进行分组,使得对数据的分类更加精确;join则用于获取两个表中匹配的关系

hive是用来分析hdfs上的结构化数据的非交互式的数据仓库,解决数据冗余,hive最终被编译成MapReduce,通过SQL执行MapReduce。

namerank
Java1
Java2
Java1
......
Hive2
Java1
Hive2
......
SELECT 
	name
   ,rank
   ,count(1) 	AS cnt 
FROM TEST 
GROUP BY name, rank;

Map阶段的key即是group by字段的组合

t_student

studentrank_id
Arvin1
Jin1
Bob2

t_rank

rank_idrank
1good
2bad
SELECT
	t.student
   ,tt.rank
FROM t_student t
JOIN t_rank tt
on t.rank_id = tt.rank_id;

常见的查询优化器

Top-down Optimizer

论文中给出的实现是一个自顶向下(top-down)的递归算法,在每个递归节点上,可以通过某些规则决定apply规则的先后顺序。

Bottom-up Optimizer

Rule-based Optimizer,RBO

  • Rule
  • Pattern

Cost-based Optimizer,CBO

  • 动态规划

交换律、结合律、传递性

RBO 优化规则

  • 列裁剪
  • 谓词下推
  • 传递闭包
  • Runtime Filter(min-max filter,in-list filter,bloom filter)
  • Join 消除
  • 谓词合并

CBO 相关概念

  • 统计信息
    • Number of Distinct Value,NDV
    • Selectivity
    • Cardinality
  • 代价模型

查询优化器的社区开源实践

Apache Calcite

Orca

Volcano/Cascade 框架

  • Memo
  • AND/OR Graph
  • Expression group
  • Group expression
  • Pattern
  • Rule
  • Branch-and-Bound Pruning
  • Winner

SQL相关的前沿趋势

存储计算分离

HSAP, HTAP, HTSAP

Cloud Native, Serverless

数据仓库,数据湖,湖仓一体,联邦查询

智能化:AI4DB,DB4AI