CMU 15445 8. Query plan

DBMS将SQL语句转换为查询计划。 Operator被安排在Tree上。 数据从叶子流向根。 树中根节点的输出是查询的结果。通常运算符是二元的(1-2个孩子)。 可以以多种方式执行相同的查询计划。 大多数DBMS都希望尽可能多地使用索引扫描。

处理模型

处理模型分为3类,迭代模型,主要是自顶向下的。物化模型,是自底向上的。矢量模型,像迭代模型一样通过next来交互,但是是批量发送数据,也是自顶向下的。
在大多数dbms的系统都支持迭代模型,物化模型更加适合oltp而非olap。而矢量模型是olap的理想选择。
我们来看下几个模型。

迭代模型

CMU 15445 8. Query plan_第1张图片
image.png

所有的代数运算符(operator)都被看成是一个迭代器,它们都提供一组简单的接口:open()—next()—close(),查询计划树由一个个这样的关系运算符组成,每一次的next()调用,运算符就返回一行(Row),每一个运算符的next()都有自己的流控逻辑,数据通过运算符自上而下的next()嵌套调用而被动的进行拉取。

每一个运算符都将下层的输入看成是一张表,next()接口的一次调用就获取表中的一行数据,这样设计的优点是:

  1. 每个运算符之间的代数计算是相互独立的,并且运算符可以伴随查询关系的变化出现在查询计划树的任意位置,这使得运算符的算法实现变得简单并且富有拓展性。
  2. 数据以row的形式在运算符之间流动,只要没有sort之类破坏流水性的运算出现,每个运算符仅需要很少的buffer资源就可以很好的运行起来,非常的节省内存资源。

但是这种运算符的嵌套模型也有它的缺点:

  1. 迭代模型的流控是一种被动拉取数据的过程,每行数据流向每一个运算符都需要额外的流控操作,所以数据在操作符之间的流动带来了很多冗余的流控指令。

  2. 运算符之间的next()调用带来了很深的虚函数嵌套,编译器无法对虚函数进行inline优化,每一次虚函数的调用都需要查找虚函数表,同时也带来了更多的分支指令,复杂的虚函数嵌套对CPU的分支预测非常不友好,很容易因为预测失败而导致CPU流水线变得混乱。这些因素都会导致CPU执行效率低下。

物化模型

CMU 15445 8. Query plan_第2张图片
image.png

这个模型主要就是底层开始驱动,把需要的数据一次性准备好喂给上层。缺点就是直接大批量的数据传输,所以不太适合olap这种在大数据集里分析的操作。

矢量模型

image.png

是迭代模型的升级版,从单个拉数据到批量。可以有效减少每一个operator invoke的次数。

数据获取方式

当数据库管理系统想要获取数据。需要去找。有3种查找方式。
分别是顺序扫描,索引扫描,和多索引扫描。

顺序扫描

顾名思义就是一个一个page查,每个page一个一个查条目。
为了提速,有如下优化方式。
•预取:提前获取下几页,以便DBMS在访问每个页面时不必阻塞。
•并行化:并行使用多个线程/进程执行扫描。
•缓冲池旁路:扫描线程将从磁盘获取的页面存储在其本地内存而不是缓冲池中。这避免了顺序泛滥问题。
•Zone map:预先计算页面中每个元组属性的聚合。然后,DBMS可以通过首先检查其区域映射来检查是否需要访问页面。每个页面的区域地图是存储在单独的页面中,每个区域映射页面中通常有多个条目。因此,可以减少顺序扫描中检查的总页数。


CMU 15445 8. Query plan_第3张图片
image.png

•延迟实现:每个Operator传递下一个Operator所需的最少量信息(例如,记录ID)。这仅适用于列存储系统(即DSM)。


CMU 15445 8. Query plan_第4张图片
image.png

•堆群集:使用群集索引指定的顺序将元组存储在堆页面中。


CMU 15445 8. Query plan_第5张图片
image.png

索引扫描

DBMS选择一个索引(或多个索引)来查找查询所需的元组。
使用多个索引时,DBMS会对每个索引执行搜索并生成一组匹配的记录ID。 可以使用位图,哈希表或布隆过滤器来实现此记录ID。 DBMS基于查询的谓词(并集与交叉)组合这些集合。 然后它检索记录并应用任何剩余的条款。 这在Postgres中称为位图扫描。
按照它们在非聚簇索引中出现的顺序检索元组是低效的。 DBMS可以首先找出它需要的所有元组,然后根据它们的页面ID对它们进行排序。

CMU 15445 8. Query plan_第6张图片
image.png

在非聚集索引上扫描的问题

image.png

你可能感兴趣的:(CMU 15445 8. Query plan)