热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

表关系join和where性能差异原因_查询性能提升利器:CirroData之joinorder选择算法...

全文约3836字,阅读约23分钟数据库查询的快慢一般情况下和查询性能有很大的关系,这也是在数据库设计时需要重点考虑的问题。CirroData数据库面向P

4a1f3d6cad2aca42c98a5da4d3182dc5.png

全文约3836字,阅读约23分钟

数据库查询的快慢一般情况下和查询性能有很大的关系,这也是在数据库设计时需要重点考虑的问题。CirroData数据库面向PB级海量数据分析应用领域,通过引入CBO,即Cost-Based Optimizer(CBO),来根据CPU、内存以及I/O的开销进行查询的优化。CirroData的CBO通过对查询语句中的表做定量分析,并利用join order选择算法评估可行的查询计划,可以在大多数情况下选出令人满意的执行计划,提高查询性能。本文将从开发者的角度介绍join order选择算法,探索其原理和实现,而这也从某一方面回答了“CirroData的查询性能为何如此高效?”这一问题。b1c5251a9bbf60d4a79bb11407357b95.png1join order选择算法的重要性

在有限的搜索空间下,表连接的排列组合数会随着表个数的增加而迅速增长,如果这个组合数量巨大,连接次数显然是一个很高的数量级,最大可能的连接次数是N!,例如参与连接的基表个数为5时,表连接次数为120次;参与连接的基表个数为10时,表连接次数达到了3628800次!

显然,数据库使用者在复杂数据查询场景下编写SQL脚本时,难以对结果进行预判。这时,就需要引入优化器,在有限的时间内找出最优的解决方案。Cost-Based Optimizer(CBO) 是数据库查询优化中一种常用的优化器,其通过对表的统计来决定对于结构化查询最有效的查询执行计划。

CBO 中的 join order 选择算法对于海量数据查询而言,是性能提升的第一重要技术手段。Join order 选择算法简单来说就是从可行的 join order 中挑选出一个最好的,但是要在有限的时间和空间下做好这件事却是数据库中的难题之一。对于复杂的Join算子优化,采用不同的join order,花费CPU资源、内存资源的差异会比较大,这也意味着不同的join order有不同的执行效率。

例如,A join B join C,A、B表都很大,C表很小,那A join B很显然需要大量的系统资源来运算,执行时间必然不会短。但是使用A join C join B的执行顺序,A与较小的C表join必然会很快得到结果,且结果集会很小,再使用生成的小的结果集与B做连接,性能显然比第一种方案好。因此找到一个好的连接顺序,对于查询的性能至关重要。

b1c5251a9bbf60d4a79bb11407357b95.png2常见的join order选择算法

目前流行的数据库中,针对join order选择普遍采用的算法包括动态规划算法、贪心算法、遗传算法等。动态规划算法需要遍历全部解空间,但一定可以获得最优解,是最优化算法的一种;而贪心算法和遗传算法都属于启发算法,即基于直观或者经验构造的算法,在可接受的花费(指计算时间和空间)下给出待解决组合优化问题每一个实例的一个可行解,该可行解与最优解的偏离程度一般不能预计。

表3-1对动态规划算法和贪心算法进行了简单的对比:

2a3b1076fd4d54041d7099a3e63f4a1c.png

常见的数据库中,PostgreSQL和GreenPlum在表数目较少时采用了动态规划算法,表数目较多时采用遗传算法来解决该问题;而MySQL则主要通过贪心算法来实现join order的选择。

我们在制定CirroData数据库的设计方案时,充分考虑到了CirroData分析型数据库面向的PB级海量数据查询场景,采用了动态规划算法和贪心算法相结合的策略,以达到最佳查询效率。

b1c5251a9bbf60d4a79bb11407357b95.png3CirroData使用的join order选择算法

动态规划算法需要遍历全部解空间,一定可以获得最优解,因此它是CirroData的首选方案。但是即使CirroData已经基于表之间的连接关系对搜索范围做了较为仔细的范围限定,算法搜索的可能组合数仍会由于表的增多,造成巨大的搜索耗时。

因此CirroData在对join order选择的过程中采取的策略是,当参与join的表个数不超过8时,采用动态规划算法,超过8时采用贪心算法。

3.1 基于动态规划的join order选择算法

当表数量较少时,CirroData采用迭代的方式,从单个表出发,先初始化基表的代价,然后尝试求两个表的最优join order,再逐步迭代求解3个表、4个表...,从而穷举得到最优的join order。

显然穷举会造成过多的join order组合,但是实际开发中会基于表之间连接顺序的限制进行剪枝操作,另外由于动态规划本身会记录重复的子问题、连接路径建立也是一个优胜劣汰的过程等因素,有些组合并不会出现,这也在一定程度上控制了算法的时间复杂度。具体可以通过下面的例子了解CirroData基于动态规划的join order选择算法的具体过程。

例:  SELECT * FROM A, B, C, D

WHERE A.col=B.col AND A.col=C.col AND A.col=D.col;

初始化——构造第一层关系,假设使用path结构存储连接路径,则叶子节点表示为path [1],在第一层中,每个关系的最优路径就是这个基表本身,如图3-1所示:

39fb2583d47b90b2a6f0ede024ae46fe.png

图3-1 待连接的基表

归纳——假设已经生成1~L-1层子树&#xff0c;则求解第L层的关系&#xff0c;在满足join key和连接顺序限制的基础上尝试生成左深树和紧密树如图3-2所示&#xff0c;直到最终找到最优join order。具体求解过程可参考表3-2&#xff0c;其中L表示当前生成连接树的总层次&#xff0c;L <&#61; 总基表数&#xff0c;其中L左表示当前待连接的关系左侧的连接树&#xff1b;L右表示当前待连接关系右侧的连接树&#xff0c;L左&#61; L - L右。

d03ca85ea4b0caa6f182473e7e157bdf.png

图3-2 左深树和紧密树

表3-2基于动态规划的join order构造过程

dedbd858dbb84800eb1fd179c09d49e4.png

为了更清楚的描述紧密树的生成方式&#xff0c;本例再增加两个参与join的基表E&#xff0c;F&#xff0c;通过表3-3描述了6个表做join的时候生成紧密树的过程。左深树的生成过程与上例相似&#xff0c;不再赘述。

表3-3 紧密树的生成

fafd90256ad3b7f07d0a335607b5af7d.png

3.2 基于贪心的join order选择算法

当表数量超过8个时&#xff0c;CirroData采取贪心算法来进行join order的选择。进行贪心选择之前&#xff0c;需要对参与join的基表&#xff0c;基于优先级规则进行排序&#xff0c;因为小表会产生更小的代价&#xff0c;在满足语义的前提下应该尽可能的把元组个数少的的基表排在前面&#xff0c;排序的优先级如下。

  • 依赖关系&#xff0c;例如A left join B&#xff0c;则B依赖于A&#xff0c;排序后A应该在B前面&#xff1b;

  • 键引用关系&#xff0c;当表A的某个列作为外连接关系连接列或者作为条件使用时&#xff0c;认为该表将来的行数有几率变小&#xff1b;

  • 表的元组数&#xff0c;表的元组数少的&#xff0c;排在前面&#xff1b;

  • 内存地址&#xff0c;比较表在内存中的位置&#xff0c;即指针地址小的在前面。

相对动态规划算法&#xff0c;CirroData基于贪心的join order选择算法不会再穷举所有的join order&#xff0c;本文通过下面的例子描述CirroData基于贪心的join order选择算法的具体过程。为了方便描述&#xff0c;例中仅以5个基表描述基于贪心的join order选择算法&#xff0c;实际应用场景中join order的构造过程与之类似。

例:  SELECT * FROM A, B, C, D,E

WHERE A.col&#61;B.col AND B.col&#61;C.col AND C.col&#61;D.col AND D.col&#61;E.col;

本例假设没有依赖关系和建引用关系&#xff0c;搜索深度为2&#xff0c;构造过程详见表3-4。其中L表示当前生成的左深树的总层数&#xff0c;best_ref表示使用深度搜索算法获得待定的局部最优解&#xff0c;最终选出的join order也会保存在这里&#xff0c;position表示在待定的局部最优解best_ref基础上贪心选择出的确定的局部最优解。

每次贪心选择之前都会将已得到的局部最优join order与即将加入的基表及其以后的depth个没有连接过的基表进行一遍深度优先搜索&#xff0c;根据表的代价和限制关系查找新的局部最优解。之所以说是局部的&#xff0c;是因为深度优先搜索的搜索深度限制&#xff0c;这个限制可以避免表数量过多造成的大数量级连接组合的情况。在深度优先搜索算法递归调用的过程中&#xff0c;深度会逐层递减&#xff0c;查询树也从叶子逐步向上层构造。

贪心算法体现在每一次深搜的结果总认为是本次循环得到的最优结果&#xff0c;并且从best_ref中取出第一个表作为确定局部最优解的一部分&#xff0c;为下一次连接提供依据。当构造的join order的搜索深度等于表的个数&#xff0c;或提前构造出最优的join order(深搜遍历到最后一层把可连接的基表用光了&#xff0c;没有可连接的基表了)时&#xff0c;join order构造过程结束&#xff0c;得到的最优join order存储在best_ref中。

表3-4 基于贪心的join order构造过程

5cf3a62373c9a8a8c3b37a5f65b5e9ae.png

通过了解算法中两个边界情况&#xff0c;可以更清晰的理解基于贪心的join order算法&#xff0c;因为其他情况皆介于二者之间。

当参与JOIN的表的个数小于depth时&#xff0c;基于贪心的join order选择算法将退化为一个深度优先的穷举算法确定最优执行计划&#xff1b;

当depth &#61; 1的时候&#xff0c;函数退化为"极其"贪婪的算法&#xff0c;每次从当前的剩余的表中取一个代价最小的&#xff0c;来扩展当前的执行计划。

b1c5251a9bbf60d4a79bb11407357b95.png4综述和展望

CirroData的Join order选择算法为执行计划找到了更好、甚至最好的连接顺序&#xff0c;在大多数情况下提升查询语句的执行效率&#xff0c;这正是CirroData开发工作者愿意深入探索的原因之一。

但是考虑到对CirroData查询性能的进一步优化&#xff0c;想要通过CBO获得更满意的性能效果还要更多的依赖基数估计和代价模型的准确性&#xff0c;但是实际上当前CirroData的基数估计模型和代价模型&#xff0c;尤其是代价模型还有待完善。

因此&#xff0c;CirroData在探索合适的join order选择算法的同时&#xff0c;如何更好的进行基数估计和查询子树各类关键算子的代价估计也是一个值得关注的问题。

参考文献

1.李海翔. 数据库查询优化器的艺术--原理解析与SQL性能优化&#xff1b;

2.张树杰. PostgreSQL技术内幕&#xff1a;查询优化深度探索&#xff1b;

3.Viktor Leis等. How Good Are Query Optimizers,Really。

e382bee28cfdb776a6447da54c8dfb2e.png




推荐阅读
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
author-avatar
心理学点滴_312
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有