关系数据库 Query_Execution

关系数据库 Query Execution 的流程

你是否也和我一样很好奇, 当我们在数据库的控制台输入一条 SQL 语句的时候, 后台发生了哪些事情呢, DBMS 就像 OS 一样, 只是 DBMS 是对数据操作的抽象, 不像操作系统, 是对整个计算机的抽象.

DBMS Query Execution 的整体流程

下图展示了 DBMS 在接受到一条应用的 SQL 指令后的执行过程.

img

上图说明了在关系型数据库中, DBMS 接收到一条 Query 语句后, 执行如下的流程:
DBMS将Quey语句翻译成特定的内部抽象语言, 通常有两种 Query Plan

  1. logical Plan: 使用关系代数描述 Query Plan, 描述关系操作符或者非关系操作符, 表示 SQL 语句在数据库表中的操作.
  2. physical plan: 描述这些操作符的实际动作

Query 处理的三个阶段

  1. 解析以及重写 Query, SQL->logical plan
  2. Query 语言优化
    logical plan -> 优化的 logical plan -> physical plan
  3. Query Execution: 在数据库中执行这个 physical plan, 就是 Processing Model 作用的位置.

Logical Plan

DBMS 在解析一台 SQL 语句的时候步骤 1, 2 类似与编译器的编译步骤, 只是 SQL 语言的解析并不是获取机器码, 而是 Query Plan 以及 Expression. 这个过程实际上是将一条 SQL 语句的动作进行拆分, 将一整套动作拆分为各个不同的动作节点(关系操作符或者非关系操作符), 每个节点代表一个需要执行的步骤.
例如, 当 DBMS 收到的语句为:

SELECT R.id, S.cdate
    FROM R JOIN S
        ON R.id=S.id
    WHERE S.value > 100

DBMS 会将这条语句解析为下面的Plan_Node 节点树, 以及节点对应的 Expression(表达式)

img

上述的 SQL 语句需要执行的动作被切分为了五个 Plan_Node 的节点, 表示需要执行的子动作. 在 DBMS 中, 有各种各样的 Plan_Node 表示执行各种不同的动作, 上面的四种Plan Nodes 只是举例, 后续的代码中我们会实现各种不同的 Plan Nodes, 表示不同的功能.

在执行上述的 SQL 语句的时候, 数据的流向是自底向上的, 也就是最下一层的节点 3 和节点 5 先执行, 每一个孩子节点会将执行的结果返回给父节点, 执行结果根据Plan_Node的不同而不同, 大多的操作型 Plan_Node 的返回结果是从数据库中读出的 Tuples, 这些返回的 Tuples 交给父节点处理. 也有一些节点返回的不是 Tuples, 例如 Insert 类型的 Plan_Node.

Plan_Node 的附属--表达式

Plan_node 表示的是关系或者非关系操作符的节点, 有些关系操作符或者非关系操作符需要特定的各种表达式, 例如谓语表达式, 数学运算表达式等, 这些表达式是这个Plan_Node的一部分, 例如在上面图中的 Fliter Plan_Node 中, value>100 就是一个谓语的 comparison expression, 这个表达式表示需要过滤掉哪些 Tuples.
后续我们还会在其他的 Plan_Node 找到他们对应的表达式.
表达式的特点是, 一个Plan_Node的表达式内部是一个树形结构, 也就是 Tree, 但是不同的 Plan_Node 之间并不存在直接关系, 在优化Plan_Node的时候可能有继承关系.

Query Execution 步骤

Processing Model 在解析节点不起直接作用, 起作用的主要阶段是优化节点(Optimization), 优化器会根据数据库的统计信息(如表大小, 索引情况)和 SQL 查询条件, 生成一个最优的执行计划, 这里可能包括选择最合适的索引, 决定连接方式等, 以尽量减少资源消耗和提升查询速度.
Processing Model 在这个阶段开始生效, 帮助定义 SQL 优化器在构建执行计划时如何考虑数据的存储和读取方式.

根据调用数据库操作符顺序的不同, 我们通常将 Processing Model 分为两类, 分别是自上而下, 与自下而上的方式, 类似于语言编译的流程.
常见的三种Execution模型是:

  1. Iterator Model
  2. Materialization Model
  3. Vectorized / Batch Model

Iterator Model

Iterator Model 也叫 Volcano or Pipeline model, 如同火山爆发一样, 是典型的自下而上的 Processing Model, 也是最常用的 Query Execution 模型, 几乎在所有的基于行的数据库中都使用的是这种模型.
迭代模型(Iterator Model) 的工作方式是为每一个数据库的操作符添加一个 Next 函数, 也就是我们的每一个Plan_Node的主体都是在执行 Next() 函数. 在我们的 Physical Query Plan 中, 迭代模型会递归的调用孩子节点的 Next() 函数, 直到根节点, 当到达叶子节点后, 开始获取数据库中的一行数据(tuple), 然后返回到父节点处理. 在读取下一个tuple之前, 每一个tuple会被尽可能地向上处理. 在基于磁盘地数据库中, 这种方式十分有效, 因为这种方法允许我们充分地使用内存中的tuples.
我们用下图描述 Iterator Model 处理地 Query 地基本流程.

img

上图中, 从右到左分别是一个数据库Query的SQL语句, Logical Query Plan 语句, 以及使用迭代模型(Iterator Model) 的Physical Query Plan. 上述的箭头可以看出迭代模型会将每次读取的tuple返回到父节点的Next()函数中.

当Iterator Model中的Next函数满足下面两个条件时, 迭代模型(Iterator Model)中的操作符可以被相互独立的实现, 不受父节点与子节点的影响, 能够实现Pipeline的效果. 这两个条件是:

  1. 在每个操作符调用Next函数的时候, Next函数仅返回一个tuple, 或者返回空(当没有tuple被返回的时候).
  2. 操作符会实现一个循环, 在每一个孩子节点上调用Next()函数, 并处理Next函数返回的tuples.
    条件 1和2 共同作用实现了 Pipeline 的效果, 这两个条件共同作用使得每次调用 Next() 函数都会返回一个 tuple, 条件2 的限制实现了 tuple 自底向上的返回, 并且并不是递归回溯式的一次性返回.

满足了上述操作符相互独立的条件后, 迭代模型在在DBMS中可以流水线式的处理Query Plan, 它可以尽可能的使用操作符处理 tuple, 在下一个操作符读取之前. 这种处理方式就是迭代模型(Iterator Model)中的Pipeline.

简单点说就是, 每次处理一行, 尽可能地将这一行的所有操作处理完, 例如在上面的图中, 在节点5中需要读取S数据库中的tuples, 假如在节点5读取到了tuple_A, 当节点4正在处理tuple_A的时候, 节点5就可以开始继续读取后面的tuples. 这也就是所谓的流水线式的并行策略, 也就是Pipeline. 但是一些操作符由于操作的特性, 需要孩子节点获取所有tuples后才可以继续执行, 例如上图中的节点2中的Join()连接操作, 常见的还有Order By, 这些操作符被称为 pipeline breakers.

输出控制, LIMIT() 操作限制可以很容易的作用到迭代模型中, 当到达LIMIT限制后不再invoke tuples即可.

Materialization Model

Materialization Model 本质上是一种特殊的迭代模型(Iterator Model), 不同点是 Materialization Model 会一次性的处理所有的输入与输出. 换句话说, Iterator Model 在每个操作的Next()函数中, 每次处理一个从孩子节点返回的tuple, 但是在Materialization Model中, 孩子节点会一次性的将孩子处理后的所有tuples返回给父节点处理. 实际过程中, DBMS在执行的过程中, 向下调用函数的时候会通知后续的操作符需要多少tuples, 因此整个Materialization Model的输出是一整个 tuple (NSM) 或者 a subset of columns (DSM).
Materialization Model 的实现流程如下:

img

可以看到每个操作符都维持一个Out数组, 用于返回处理的所有的tuples, 同时还有一个Output函数, 处理所有的tuples. 一个孩子节点将Output的结果返回给父节点.

每一个操作符会带有一个Output函数, 这个Output函数满足下面的特点:

  1. 操作符一次性处理所有孩子节点的tuples.
  2. 该操作符的Output函数返回该操作可以返回的所有的tuples, 当操作结束执行后, 不会再生成任何新的tuple.
    这种方式更适合OLTP形式的数据库, 因为OLTP模式的数据库会存在高频率的数据增删改查, 而Materialization Model直接全部处理掉, 不需要像Iterator Model一样每次读一个.

Vectorization Model

与迭代器模型一样, 矢量化模型(Vectorization Model)中的每个运算符都实现了一个 Next 函数. 但是, 每个运算符都会发出一批数据(即向量), 而不是单个元组. 运算符的内部循环实现针对处理一批数据(而不是一次处理单个项目)进行了优化, 而且批次的大小可能因硬件或查询属性而异.

矢量化模型是为OLAP类型数据库设计的, 因为该模型可以通过很少次数的Next函数的调用来读取数据库中大量的tuples.

posted @ 2024-11-12 08:31  虾野百鹤  阅读(88)  评论(0)    收藏  举报