第十讲 Query Execution Part 1

2024-04-07 18:20
文章标签 part execution query 第十

本文主要是介绍第十讲 Query Execution Part 1,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1 处理模型【Processing Model】

DBMS 的处理模型【Processing Model】定义了系统如何执行【execute】查询计划【Query Plan】。

  • 针对不同的工作负载进行不同的权衡。

方法1:迭代器模型【Iterator Model】
方法2:物化模型【Materialization Model】
方法3:矢量化/批处理模型【Vectorized / Batch Model】

1.1 迭代器模型

迭代器是最简单的模型。他是一种自顶向下的方法。

每个查询计划【Query Plan】的(物理)操作符【Operator】都实现一个 Next() 函数。

  • 在每次调用时,操作符返回单个元组(需要注意的是,这个元组可以是关于列的一个子集,也可以是 Record ID),如果没有更多元组,则返回 eof 标记。
  • 操作符实现一个循环,即查询计划树中的父操作符在在其子节点的操作符上调用 Next() 来检索其输出元组,然后处理它们。

每个操作符的实现还具有 Open() 和 Close() 函数,它们类似于构造函数和析构函数,但这是用于操作符的罢了。
迭代器模型也称为火山模型【Volcano Model】或管道模型【Pipeline Model】。

如今大多数 DBMS 都使用这种方法,,它使得元组流水线【pipeling】成为可能。
许多运算符必须阻塞,直到他们的子操作符发出所有元组,赭红操作符也被称为 Pipeline Breaker:

  • 连接【Joins】,
  • 子查询【Subqueries】
  • 排序【Order By】

使用这种方法可以轻松进行输出控制。比如我们在查询【Query】中添加 LIMIT 10 子句,那么点那个我们的根操作符在迭代了 10 次后就可以停止了,我们不需要再继续调用 Next 了。

迭代器的约束是:我们一次只能处理一个元组,我们通过 Next 获取一个元组,然后可能会去做一些成本很高的操作,如果我们可以批量处理,那么就可以摊销很多的成本。

1.2 物化模型

物化模式是一种自底向上的方法。当父操作符调用子操作符时,他总是生成他可以生成的所有结果,然后上推给它的父擦作符。

每个操作符一次处理所有输入,然后一次发出所有输出。

  • 操作符将其输出“物化”为单个结果。
  • DBMS 可以下推提示【push down hints】(例如 LIMIT)以避免扫描太多元组。
  • 可以发送物化后的行或者单个行。

输出可以是整个元组 (NSM) 或列的子集 (DSM)。

在该模型下,我们经常会用到内链【inline】,比如图中的 S 表假设有亿万数据,我们将所有元组加载到内存,然后一次返回给父操作符,这种方法太愚蠢了,因此我们会将某些操作符进行内联。

物化模型更适合 OLTP 工作负载,因为查询一次仅访问少量元组。

  • 更低的执行/协调开销。
  • 更少的函数调用/操作符调用

但是不适合具有大量中间结果的 OLAP 查询。 

1.3 矢量模型

矢量模型就像迭代器模型一样,每个运算符都实现一个 Next() 函数,但是每个操作符发出一批元组而不是单个元组。

  • 每个运算符在内部作循环中,一次处理多个元组来加快速度(由于我们一次可以看到所有的数据,我们可以做一些矢量化操作,比如SIMD)
  • 批次的大小可能因硬件或查询属性而异

矢量化模型非常适合 OLAP 查询,因为它大大减少了每个操作符的调用次数,并使得操最符更轻松地使用矢量化 (SIMD) 指令来处理批量元组。 

1.4 PLAN PROCESSING DIRECTION

方法1:自上而下:

  • 从根开始,从其子节点“提取”数据。
  • 元组总是通过函数调用传递。

方法2:自下而上

  • 从叶节点开始,将数据推送到其父节点。
  • 允许对管道中的缓存/寄存器进行更严格的控制。
  • 更适合动态查询重新优化。

但是,大多数系统都是自顶而下的。

2 访问方法【Access Method】

访问方法是 DBMS 访问存储在表中的数据的方式,它在关系代数中未定义。

三种基本的访问方法:

  • 顺序扫描【Sequential Scan】
  • 索引扫描【 Index Scan】(许多变体)。
  • 多索引扫描【Multi-Index Scan】

2.1 顺序扫描【Sequential Scan】

对于表中的每一页【Page】:

  • 从缓冲池中检索它
  • 迭代每个元组并检查是否需要包含它

DBMS 维护一个内部游标,用于跟踪它检查过的最后一个页【Page】/槽【SLot】。、

这几乎总是 DBMS 在执行查询时可以做的最糟糕的事情,但它有时候可能是唯一可用的选择(比如当我们没有任何索引时)。
顺序扫描的优化:

  • 预取【Prefetching】
  • 缓冲池绕过【Buffer Pool Bypass】
  • 并行化【Parallelization】
  • 堆聚簇【Heap Clustering】
  • 延迟物化【 Late Materialization】
  • 数据跳过【Data Skipping】
数据跳过

方法1:近似查询【Approximate Queries】(有损)

  • 对整个表的采样子集执行查询以产生近似结果。
  • 示例:BlinkDB、Redshift、ComputeDB、XDB、Oracle、Snowflake、Google BigQuery、DataBricks

方法2:区域地图【Zone Map】(无损)

  • 预先计算每页的列式聚合【Aggregation】,DBMS 首先检查区域地图【Zone Map】来决定是否要访问该页【Page】。
  • 需要在页面大小与过滤器效能之间进行权衡,因为页面越大,数据也就越多,如果在一个有百万数据的大页上,我们根据区域地图算出需要访问该页,但是该页中可能只有一条数据满足查询条件,但是我们不得不扫描完整的页。
  • 示例:Oracle、Vertica、SingleStore、Netezza、Snowflake、Google BigQuery

2.2 索引扫描【 Index Scan】

DBMS 选择一个索引来查找查询【Query】所需的元组。
使用哪个索引取决于(所有这些都是后面章节里会讲,大概是第12讲):

  • 索引包含哪些属性【attribute】
  • 查询【Query】引用哪些属性【attribute】
  • 属性【attribute】的值域
  • 谓词组合【Predicate composition】
  • 索引是否有唯一键或非唯一键

假设我们有一个包含 100 个元组和两个索引的表:
→ 索引1:age
→ 索引2:dept

SELECT * FROM students

WHERE age < 30

AND dept = 'CS'

AND country = 'CHINA'

场景1:30岁以下的有99人,但是CS部门只有2人。 

在该场景下,我们希望可以选择 dept 索引,因为他的选择性更好,可以顾虑更多的数据

场景1:30岁以下的有 2人,但是CS部门有99人。 

在该场景下,我们希望可以选择 age 索引,因为他的选择性更好,可以顾虑更多的数据

2.3 多索引扫描【Multi-Index Scan】

如果 DBMS 可以在一个查询【Query】中使用多个索引:

  • 使用每个匹配的索引计算 Record ID 的集合。
  • 根据查询【Query】的谓词,来组合这些集合(并集与交集)
  • 检索记录并应用剩余的谓词

实现的数据库:

  • DB2 多索引扫描
  • PostgreSQL 位图扫描
  • MySQL 索引合并

SELECT * FROM students

WHERE age < 30

AND dept = 'CS'

AND country = 'CHINA'

继续复用前面的 age 和 dep 的例子:

  • 我们可以使用第一个检索 idx_age 来匹配满足 age < 30 的 Record ID
  • 然后使用第二个检索 idx_dep 来匹配满足 dept = 'CS' 的 Record ID,
  • 取它们 的交集
  • 检索记录,并检查 country = 'CHINA'

可以使用位图【Bitmap】或哈希表【Hash Table】有效地完成集合交集。

3 修改查询【Modification Query】

修改数据库的操作符(INSERT、UPDATE、DELETE)需要负责修改目标表【table】及其索引【index】,约束检查可以立即在操作符内部发生,也可以推迟到稍后的查询【Query】/事务【Transction】中。


这些运算符的输出可以是 Record ID 或元组数据(即 RETURNING)。

UPDATE/DELETE:

  • 子操作符传递目标元组的 Record ID
  • 必须追踪每一个先前看到过的元组(下面例子里会讲,这也是万圣节问题的解决方案)。

INSERT:

  • 选择1 :在操作符内部物化元组
  • 选择2:操作符插入从子操作符传入【Pass In】的任何元组。

例子:

1️⃣ 假设我们有一张perople表,表中记录了薪水

2️⃣ 现在我想对所有薪水小于 1100 的人作一次 100 元的薪水普调,那么现在我的更新操作符会调用它的子操作符的 Next 方法,该子操作符执行一个索引扫描,以超出所有薪水小于 1100 的工人

 

3️⃣ 在索引扫描中,我们跟随游标遍历数据,并将满足条件的元组返回给父操作符

4️⃣ 然后父操作符将它从索引内删除,然后插入一条更新后的索引


5️⃣ 然后索引游标继续滑动,但是我们会查到刚才插入的那条数据

这就是著名的万圣节问题【HALLOWEEN PROBLEM】,即:更新操作更改元组的物理位置,导致扫描操作符多次访问该元组的异常。

  • 可能发生在聚簇表或索引扫描上。

IBM 研究人员于 1976 年万圣节在 System R 上工作时首次发现。
解决方案:跟踪每个查询【QUery】中修改的 Record ID。 

4 表达式评估【Expression Evaluation】

DBMS 将 WHERE 子句表示为表达式树【Expression Tree】。我们前面看到查询计划【QUery Plan】是一颗关于操作符的数,在每个操作符内部依然可以是树结构。

表达式树中的节点代表不同的表达式类型:

  • 比较(=、<、>、!=)
  • 合取 (AND)、析取 (OR)
  • 算术运算符(+、-、*、/、%)
  • 常数值
  • 元组属性引用【Tuple Attribute Reference】

例子:

1️⃣ 下面是我们要执行的 Prepared 语句,我们调用execute执行它

 2️⃣ 下面是该SQL中的表达式树:

以这种方式评估谓词的速度其实很慢,DBMS 遍历树,对于它访问的每个节点,它必须弄清楚操操作符需要做什么。

我们来考虑这个谓词:WHERE S.val=1

更好的方法,或者说是优化吧,就是直接计算表达式,回想一下 JIT 编译,Postgre 中就有这个特性,它会将表达式部分内联编译(set jit = 'on')。

5. Schedule

到目前为止,我们基本上已经从数据流【data flow】的角度了解了查询处理模型【query processing model】。
控制流【conrol flow】隐含在处理模型【processing model】中。 我们可以使用调度程序【scheduler】使控制流更加明确。
数据库论文中通常不会讨论查询调度程序【scheduler】。 我们将看看 Quickstep(学术)项目中做了什么。 基于允许数据流和控制流之间的频繁切换。

这篇关于第十讲 Query Execution Part 1的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/883328

相关文章

SpringBoot基于MyBatis-Plus实现Lambda Query查询的示例代码

《SpringBoot基于MyBatis-Plus实现LambdaQuery查询的示例代码》MyBatis-Plus是MyBatis的增强工具,简化了数据库操作,并提高了开发效率,它提供了多种查询方... 目录引言基础环境配置依赖配置(Maven)application.yml 配置表结构设计demo_st

Level3 — PART 3 — 自然语言处理与文本分析

目录 自然语言处理概要 分词与词性标注 N-Gram 分词 分词及词性标注的难点 法则式分词法 全切分 FMM和BMM Bi-direction MM 优缺点 统计式分词法 N-Gram概率模型 HMM概率模型 词性标注(Part-of-Speech Tagging) HMM 文本挖掘概要 信息检索(Information Retrieval) 全文扫描 关键词

MySQL record 02 part

查看已建数据库的基本信息: show CREATE DATABASE mydb; 注意,是DATABASE 不是 DATABASEs, 命令成功执行后,回显的信息有: CREATE DATABASE mydb /*!40100 DEFAULT CHARACTER SET utf8mb3 / /!80016 DEFAULT ENCRYPTION=‘N’ / CREATE DATABASE myd

Vue3图片上传报错:Required part ‘file‘ is not present.

错误 "Required part 'file' is not present" 通常表明服务器期望在接收到的 multipart/form-data 请求中找到一个名为 file 的部分(即文件字段),但实际上没有找到。这可能是因为以下几个原因: 请求体构建不正确:在发送请求时,可能没有正确地将文件添加到 FormData 对象中,或者使用了错误的字段名。 前端代码错误:在前端代码中,可能

构建现代API:FastAPI中Query与Body参数的最佳搭配

在FastAPI中,Query 和 Body 是两种不同的依赖注入器,它们的应用场景取决于你的具体需求。以下是它们各自常见的使用场景: Query 参数 使用场景: 当你需要从URL中获取一些简单的参数时,例如过滤、排序、分页等。 当数据量不大,且可以作为URL的一部分安全传输时。当数据不需要复杂的结构时。 Body 参数 使用场景: 当你需要发送较为复杂的数据结构时,例如包含多个字段

兔子--Android Studio出现错误:Error:Execution failed for task ':myapp:dexDebug'. com.android.ide.common.pro

重点在:finished with non-zero exit value 2. 这里表明了有重复的内容存在。 由于:Android Studio中引入包的方式有如下2种:    compile 'com.android.support:support-v4:22.0.0'    compile files('libs/support-v

C++入门(part 2)

前言 在前文我们讲解了C++的诞生与历史,顺便讲解一些C++的小语法,本文会继续讲解C++的基础语法知识。 1. 缺省参数 1.1缺省参数的概念 缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调⽤该函数时,如果没有指定实参则采⽤该形参的缺省值,否则使用指定的实参。(有些地⽅把缺省参数也叫默认参数) 1.2 缺省参数的分类 缺省参数分为全缺省和半缺省参数,全缺省就是全部形参给

MySQL record 01 part

更改密码: alter user 'root'@'localhost' identified with mysql_native_password by ‘123456’; 注意: 在命令行方式下,每条MySQL的命令都是以分号结尾的,如果不加分号,MySQL会继续等待用户输入命令,直到MySQL看到分号,才会去执行分号前的所有用户输入的语句。包括密码在内,用户名、主机名,都需要使用引

【硬刚ES】ES基础(二十) 单字符串多字段查询:Dis Max Query

本文是对《【硬刚大数据之学习路线篇】从零到大数据专家的学习指南(全面升级版)》的ES部分补充。

【硬刚ES】ES基础(十九) Query Filtering 与多字符串多字段查询

本文是对《【硬刚大数据之学习路线篇】从零到大数据专家的学习指南(全面升级版)》的ES部分补充。