物化视图和视图的最大区别_基于catalyst的物化视图改写引擎的实现

更新日志:
1. 2020/06/16 group by 视图的部分描述错误,已修正。

什么是物化视图

我先用我的话解释一下什么是物化视图。假设我们已经有A,B两张表,现在我创建了一张表C,

C是由A,B两张表经过一条SQL处理得到的,这个时候我们就可以认为C是A,B的物化视图了。那怎么用呢?当一个用户写了一条使用A Join B表的SQL,系统会自动尝试能否改写成基于C表的查询,如果成功,那么可能查询速度就非常快了,因为避免了Join的发生,只是简单的基于C做了下过滤,但得到的结果和直接使用A,B 是一样的。

显然,物化视图有个很大的问题,就是更新问题,譬如A,B发生了变化,如何保证C 也得到更新。所以这里除了改写以外,还涉及到了C的创建,管理和更新问题。

现在让我们引入点术语了,前面我们提到的自动将基于A,B的查询改写成基于C的查询,我们叫Query Rewrite。Query Rewrite 就是将原有的查询不需要修改,引擎自动选择合适的物化视图进行查询重写,完全对应用透明。

物化视图和传统视图的最大的区别是,物化视图存储不仅存储了计算逻辑,还存储了计算结果,并且更进一步的是,作为用户你无需显示使用物化视图,系统会通过Query Rewrite自己来完成内部的改写。不过无论技术上多先进,我们最后都可以归纳到以空间换时间里去。

SQL Booster

今天我们探讨的重点是如何实现Query Rewrite。我去年写了一个Query Rewrite 引擎[s

ql-booster](https://github.com/aistack/sql-booster),其实是受到阿里李呈祥团队的relational cache启发。当时看了他们的分享觉得太棒了,很想立马就用,但是想着等他们推到开源项目里就太漫长了,加之目前大数据里的物化视图的实现,已经开源的貌似只有hive了,是基于Calcite实现的,而Spark 的话是自己开发的catlyst引擎,而我自己又重度使用Spark,所以干脆自己动手基于catalyst实现一个。后面在开发过程中也遇到了不少公司也在做类似的实现,也有问我的,可惜一直没有写文章,这次趁着周末,写了,既可以做为交流用,也可以作为备忘录。因为时间久了,代码和思路就很容易遗忘,文章可以很容易唤醒记忆,一箭双雕。

知识准备篇

一个物化视图由两部分构成:

1. 生成该物化视图的SQL

2. 表数据

表数据很简单,就是为了查询的。记录生成该物化视图的SQL的原因是,我们需要知道这个物化视图的数据是来源哪些表的,每个字段的是来源哪些表,不然没办法做改写。

Query Rewrite的基本步骤如下

1. 注册各个视图,这些视图都会以AST(Catalyst里的LogicalPlan)存在

2. 待改写的用户SQL,这些SQL不会显示使用物化视图。

3. 将SQL解析成方便遍历处理的AST,也是Catalyst里的LogicalPlan,并且经过Analyzed的,因为我们需要明确知道每个字段属于哪个表。

4. 处理待改写的LogicalPlan,然后去和每个已经存在的视图LogicalPlan匹配,对于匹配上的,则实行改写

5. 最后将该写过的LogicalPlan重新生成SQL或者直接执行得到结果。

所以实际上,上面涉及到如下几个概念,大家需要有个基本感知:

1. 所有视图的LogicalPlan

2. 待改写的查询LogicalPlan

Query Rewrite 的分而治之

在思考Query Rewrite实现的的时候,我想到的第一个问题就是,一条待改写的SQL是不是可能会使用到多个视图?

答案是肯定的。理由有三:

1. 如果一条SQL只匹配一个视图,如果该视图能覆盖到这条SQL的大部分表,那么该视图的通用性必然不好。如果只能覆盖SQL里的一小部分,那么如果只匹配到一个视图,那么可能最后查询速度的提升不会太好。

2. 匹配度太低,还会导致大量存储的使用,否则就相当于不起作用了。

3. 对于一条复杂的SQL,里面会包含各种子查询,所以作为一个整体的SQL去匹配一个视图,实现上也是有难度的。

实际上,一条SQL,其复杂度主要来源于子查询和join。 join是我们需要尽量通过物化视图消解掉的,而子查询,本质上就是SQL内置的虚拟视图,我们希望尽可能通过物化视图来替换掉这些虚拟视图(虚拟视图意味着大量的计算,因为虚拟视图里一般也会有复杂的Join查询)。这样,事情就变得简单了,我们只要把一条SQL里的所有子查询都拎出来,最后每个句子都会符合SPJG形式。

所谓SPJG形式的语句是指仅包含如下语法块的语句:

1. project

2. agg

3. filter

4. join

其他如limit,Order by之类的并不影响视图替换,我们无需考虑。

如果把子查询都拉出来,最后会形成一个子查询树状结构,理论上我们只要对叶子节点做处理即可(只包含基础表的SPJG语句),每个叶子节点一定是符合SPEG格式要求的。当然了,如果我们的物化视图还带有层级结构,也就是基于物化视图上再生成新的物化视图,那么还可以进一步按现在的逻辑匹配。不过我们先不搞他。我们先只处理非视图表替换成视图表的情况。

有了上面的思路,事情就简单了,因为我们是对很简单的SQL语句做视图替换匹配,而且因为一个复杂的SQL会包含很多只包含了基础表的SPJG语句,我们一一尝试用物化视图替换他们就好。

具体做法是,我们把SQL先用Catalyst解析成 Analyzed LogicalPlan,另外我们还要做一些适当的优化,我目前是做了EliminateOuterJoin,PushPredicateThroughJoin,这样可以将多种原先形式不同的 LogicalPlan 转化成相同的形式,可以提高命中率。得到了这个语法树后我们通过AST提供的transformDown/transformUp拿到所有符合SPEG形态的语句。接着拿着这些SPEG语句一一去匹配是不是有符合的物化视图。

接下来我们在具体看SEPG具体匹配和修改逻辑的之前,我们还需要解决一个问题,我们可能有几十个甚至上千个物化视图,一一去匹配效率肯定是不行的,如果快速缩小范围呢?

一个简单的视图倒排索引

我们在创建物化视图的时候,系统会自动拿到视图里的主表,也就是join最左侧的表。如果该主表被多个视图包含,最终会形成下面的结构:

主表 -> 视图1, 视图2,视图3... 

注意,这里的主表和视图,都是Catalyst里的LogicalPlan。

当我们在处理SPEG 语句的时候,我们也按相同的方式拿到主表,然后以它为key去拿到对应的视图,这个过程是非常快的。得到视图后,我们会遍历这些视图,去看这些视图里的表是不是和SPEG里出现的表是一样的,如果是一样,就算匹配上了。完全匹配上了的视图,可能也会有多个,然后我们会进一步做测试他们的等价性,如果只有一个匹配上,那万事大吉,做改写就好,如果还有多个匹配上,那么可能就需有个打分模型了,不过我们也可以简单的取第一个匹配上的就完事。

当然了,如果你不怕空间浪费,也可以将每个视图涉及到的表都拿出来做形成前面的结构,性能上应该会更好,但是内存可能消耗会大一点,这个就要考实现者自己权衡了。

如何将SPEG使用物化视图进行改写

改写其实是要经历两个阶段的,第一个是匹配阶段,第二个才是改写阶段。

因为SPEG组成已经比较简单了,因为只包含了project/agg/filter/group/join 等几个部分。所以我们匹配和改写主要就是针对这么几个部分。这意味着我们至少需要五个匹配器,五个改写器。然后执行逻辑是,五个匹配器都去匹配,只有都符合了,才会触发五个改写器进行改写

下面是sql-booster的匹配器和改写器。

val pipeline = buildPipeline(rewriteContext: RewriteContext, Seq(//filter条件子句的matcher/rewritenew PredicateMatcher(rewriteContext),new SPGJPredicateRewrite(rewriteContext),//groupby 条件子句的matcher/rewritenew GroupByMatcher(rewriteContext),new GroupByRewrite(rewriteContext),//聚合子句的matcher/rewrite new AggMatcher(rewriteContext),new AggRewrite(rewriteContext),//join子句的matcher/rewritenew JoinMatcher(rewriteContext),new JoinRewrite(rewriteContext),//select子句的matcher/rewritenew ProjectMatcher(rewriteContext),new ProjectRewrite(rewriteContext)))

每个匹配器都需要实现一定的规则。比如where条件子句要求视图的过滤子句必须包含查询SQL的。什么意思呢?比如假设我们有基础表A,用户的原生查询如下:

select A.a from A where A.a<10; 

物化视图C的定义是:

select a from A where a<11; 

显然,在filter(where)里,C的数据集是包含用户原生查询的,所以对于where条件我们除了替换成C的a属性以外,其他的都不用动。

select a from C where a<10; 

实际上,对于这一个简单的语句,我们至少需要检查如下两点:

1. 用户的project属性是不是都在 C的project属性(select语句里的属性)里

2. 用户的filter的过滤范围是不是都在C的filter过滤方位内。

对于join语句,因为前面我们使用了优化器,会将Join On后面的条件语句进行合理下推,在LogicalPlan里会转化成filter,所以我们只要判断Join语法书是不是一致即可。

对于带有agg/group 的则比较复杂。

比如视图C由如下SQL得到:

select m,c,count(*) as a from A group by m,c

用户查询语句如下:

select c,count(*) as a from A group by c  

这个时候匹配比较容易,我们需group by语句的条件视图是用户查询语句的超集,并且顺序必须是后面的。比如视图里是group by m,c 那用户的查询只能是m,c 或者c,同时依然要符合视图的project属性要包含用户的所有的project属性,但是改写上会麻烦一些,需要改写成如下形式才会是等价的:

select sum(a) from C group by c  

另外一个例子是avg,他最后要改写成sum(k)/a(a等于视图里的avg(k))。具体的一些改写规则我在文章中就不一一罗列,大家感兴趣可以去看看我上面罗列的五个改写器。

注意: 名词filter/predicate 是等价的,一般是指过滤条件;project 是指select语句部分;

如何从LogicalPlan转化会SQL

我其实我希望sql-booster是一个标准的Query Rewrite服务。只要把表和视图的定义注册进来,给定一条SQL,就能返回一条改写后的SQL。所以如何把LogicalPlan转换回SQL也是一个比较重要的工作。正如我们前面讨论的,无论SQL多复杂,最后都是由SPEG的树状结构构成,所以我们还原的语句其实会比较简单,核心就是递归处理子查询,把每个子查询都转化成一个标准的SPEG语句。

比较繁琐的是表达式需要还原回字符串,这个需要大量的枚举。具体参看org.apache.spark.sql.catalyst.sqlgenerator.LogicalPlanSQL,该代码主要修改自Moonbox项目,对此表示感谢。

当然了,很多情况我们可能也不需要这个步骤,仅仅需要直接执行改写后的LogicalPlan或者序列化LogicalPlan后直接发回执行即可。

最后的结束语

物化视图的Query Rewrite是个需要积累的活,目前sql-booster仅仅是实现了部分匹配/改写规则,毕竟当时自己好像只开发一到两个礼拜。不过后续我有时间会继续完善,也希望能够在公司应用起来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446109.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux加密框架中的算法和算法模式

参考链接 Linux加密框架中的算法和算法模式&#xff08;三&#xff09;_家有一希的博客-CSDN博客 对称算法 14 如上所示&#xff0c;在arc4.c中定义了两个与RC4算法相关的算法实现&#xff0c;分别为arc4和ecb(arc4)&#xff0c;其中arc4是RC算法的算法实现&#xff0c;而ecb…

python学籍管理系统 flask_taskday05-Python之flask学习 web开发最基本的需要(特别详细且适用)...

1.首先一个Flask的Web项目的创建需求一(文章概述)&#xff1a;一&#xff1a;必须实现命令工具管理App&#xff0c;用于在命令行输入命令对项目进行管理&#xff0c;对后期多多益善二&#xff1a;必须实现“蓝图”管理&#xff0c;用于将app启动函数与路由分开管理&#xff0c;…

Linux加密框架crypto AES代码相关

例子 aes_generic.c - crypto/aes_generic.c - Linux source code (v5.15.11) - Bootlin static struct crypto_alg aes_alg {.cra_name "aes",.cra_driver_name "aes-generic",.cra_priority 100,.cra_flags CRYPTO_ALG_TYPE_CIPHER,.cra_blocks…

python语言print函数_Python 的 print 函数

Python 2.x 系列已经停止维护了&#xff0c; python 3.x 系列正在成为主流&#xff0c;尽管有些项目还是python2.x 的&#xff0c;之后写Python 代码为了保持兼容性&#xff0c;还是尽量和Python 3 标准保持一致作为一个Python newbee 而言&#xff0c; python 2.x 和 3.x 的 …

Linux加密框架crypto crypto_alg|cipher_alg数据结构|AES例子

加密框架将算法的属性抽象为算法说明数据结构struct crypto_alg&#xff0c;加密框架中的每一个算法&#xff08;基础算法和衍生算法&#xff09;都表示为一个算法说明数据结构的实例&#xff0c;因此将struct crypto_alg称为通用算法说明数据结构。后续章节中如无特殊说明&…

python如何运用ols_使用OLS回归(Python,StatsModels,Pandas)预测未来值

我目前正试图在Python中实现一个MLR&#xff0c;我不知道如何去应用我发现的未来值的系数。使用OLS回归(Python&#xff0c;StatsModels&#xff0c;Pandas)预测未来值import pandas as pdimport statsmodels.formula.api as smimport statsmodels.api as sm2TV [230.1, 44.5,…

Linux加密框架 crypto RC4

参考链接 arc4.h Linux加密框架中的主要数据结构&#xff08;一&#xff09;_家有一希的博客-CSDN博客 头文件 arc4.h - include/crypto/arc4.h - Linux source code (v5.15.11) - Bootlin实现代码 arc4.c arc4.c - crypto/arc4.c - Linux source code (v5.15.11) - Bootlin…

python读txt转array_python将txt文件读入为np.array的方法

原文件&#xff1a;7.8094,1.0804,5.7632,0.012269,0.008994,-0.003469,-0.79279,-0.064686,0.11635,0.68827,5.7169,7.9329,0.010264,0.003557,-0.011691,-0.57559,-0.56121,原文件数据比较多&#xff0c;是一个125行&#xff0c;45类float数字。代码&#xff1a;# -*- coding…

Linux加密框架 crypto 哈希算法说明 同步哈希shash_alg | 异步哈希 ahash_alg | 通用部分抽象 hash_alg_common

参考链接 Linux加密框架中的主要数据结构&#xff08;二&#xff09;_家有一希的博客-CSDN博客 定义 通用算法说明数据结构crypto_alg的联合体成员变量cra_u中包含多种算法的个性化属性&#xff0c;如分组算法、块加密算法、压缩算法、伪随机数算法等&#xff0c;但不包含哈希…

python 列表间隔取值_python list数据等间隔抽取并新建list存储的例子

原始数据如下&#xff1a;[e3cd, e547, e63d, 0ffd, e39b, e539, e5be, 0dd2, e3d6, e52e, e5f8, 0000, e404, e52b, e63d, 0312, e38b]将其分割为4路数据&#xff0c;分别存储在fetal1、fetal2、mother1、ECG的列表中&#xff0c;各列表对齐&#xff0c;不能整除于4的数据舍去…

Linux加密框架 crypto 哈希算法举例 MD5

参考链接 Linux加密框架 crypto 哈希算法说明 同步哈希shash_alg | 异步哈希 ahash_alg | 通用部分抽象 hash_alg_common_CHYabc123456hh的博客-CSDN博客Linux加密框架中的主要数据结构&#xff08;二&#xff09;_家有一希的博客-CSDN博客 MD5 md5.h - include/crypto/md5.h …

事务没提交的数据查的出来吗?_“金三银四”面试官:说说事务的ACID,什么是脏读、幻读?...

一、事务事务是数据库管理系统执行过程中的一个逻辑单位&#xff0c;由一个有限的数据库操作序列构成。--摘自百科在MySQL里&#xff0c;事务是在引擎层面实现&#xff0c;比如MyIsam不支持&#xff0c;InnoDB支持面试清单&#xff08;Java岗&#xff09;&#xff1a;JavaJVM数…

Linux加密框架 crypto 算法模板

参考链接 Linux加密框架中的主要数据结构&#xff08;三&#xff09;_家有一希的博客-CSDN博客algapi.h - include/crypto/algapi.h - Linux source code (v5.15.11) - Bootlin 定义 struct crypto_template {struct list_head list;struct hlist_head instances;struct modu…

python找最长的字符串_为Python找到最长重复字符串的有效方法(从Pearls编程)

我的解决方案是基于后缀数组。它是由最长公共前缀的两倍前缀构成的。最坏情况下的复杂度是O(n(logn)^2)。任务”伊利亚特.mb.txt“在我的笔记本上花了4秒钟。代码在函数suffix_array和longest_common_substring中有很好的文档记录。后一个函数很短&#xff0c;可以很容易地修改…

Linux加密框架 crypto 算法模板 CBC模板举例

参考链接 Linux加密框架中的主要数据结构&#xff08;三&#xff09;_家有一希的博客-CSDN博客https://blog.csdn.net/CHYabc123456hh/article/details/122194754 CBC算法模板 cbc.c - crypto/cbc.c - Linux source code (v5.15.11) - BootlinCBC算法模板属性 1)CBC算法模板名…

leetcode数组汇总_LeetCode刷题实战43:字符串相乘

算法的重要性&#xff0c;我就不多说了吧&#xff0c;想去大厂&#xff0c;就必须要经过基础知识和业务逻辑面试算法面试。所以&#xff0c;为了提高大家的算法能力&#xff0c;这个公众号后续每天带大家做一道算法题&#xff0c;题目就从LeetCode上面选 &#xff01;今天和大家…

Linux加密框架 crypto 算法模板 HMAC模板举例

参考链接 Linux加密框架中的主要数据结构&#xff08;三&#xff09;_家有一希的博客-CSDN博客Linux加密框架 crypto 算法模板_CHYabc123456hh的博客-CSDN博客 HMAC算法模板 hmac.c - crypto/hmac.c - Linux source code (v5.15.11) - Bootlinhmac.c - crypto/hmac.c - Linux…

判断非负整数是否是3的倍数_五年级数学因数与倍数知识点汇总与解题方法技巧...

在日常教学过程中&#xff0c;我发现孩子们和某些家长对学习数学的方法有一些误区&#xff0c;就是觉着数学&#xff0c;单纯就是逻辑思维&#xff0c;只要多做练习题就能学好&#xff0c;但是不是这样的&#xff0c;低年级的学生&#xff0c;学习数学还是以背诵为主&#xff0…

tcp通讯一次最多能发送多少数据?_关于TCP/IP,必须知道的十个知识点

本文整理了一些TCP/IP协议簇中需要必知必会的十大问题&#xff0c;既是面试高频问题&#xff0c;又是程序员必备基础素养。一、TCP/IP模型TCP/IP协议模型&#xff08;Transmission Control Protocol/Internet Protocol&#xff09;&#xff0c;包含了一系列构成互联网基础的网络…

Linux内核crypto子系统的调用逻辑

testmgr.c - crypto/testmgr.c - Linux source code (v5.15.11) - Bootlin上述代码是内核内部即crypto子系统对外提供密码服务的测试程序调用流程&#xff1a;crypto API <—> crypto core <—> crypto_register_alg处于用户态的程序想要调用处于内核态的密码算法&…