第八讲 Sort Aggregate 算法

news2024/11/26 13:55:44

我们现在将讨论如何使用迄今为止讨论过的 DBMS 组件来执行查询。

1 查询计划【Query Plan】

我们首先来看当一个查询【Query】被解析【Parsed】后会发生什么?

当 SQL 查询被提供给数据库执行引擎,它将通过语法解析器进行检查,然后它会被转换成关系的代数表示,需要注意的是,这个代数表示是得到优化过后的。

大概解释一下这棵树:

  • R 和 S 是两张表,也可以说是两个关系
  • 首先对 S 表做查询,条件谓词 value > 100
  • 然后将 S 表与 R 表做 join ,连接的条件是 S.id = R.id
  • 最后对联表生成的关系做映射,只取R.id和S.cdata两列输出的到结果中

我们得到了用树形表示的查询,树的每一个节点都是一个操作符。数据从树的叶子向上流向根,根节点的输出就是查询的结果。
 

那么我们想用算法对这些操作符做什么呢??

  • 就像不能假设表完全可以被内存容纳下来一样,面向磁盘的 DBMS 也不能假设查询结果(设置时中间结果)可以被内存容纳。
  • 我们将使用缓冲池来实现需要溢出到磁盘的算法,即内存不足以承载(中间)结果集时。
  • 我们还将更喜欢能够最大化顺序 I/O 量的算法。

2 为什么我们需要排序

关系型模型【Relational model/】/SQL 是无序的【unsorted】。
但是查询【Query】可能要求元组以特定方式排序(ORDER BY)。
但即使查询没有指定顺序,我们可能仍然想要排序来做其他事情:

  • 轻松支持重复消除(DISTINCT)。
  • 将排序元组【sorted tuples】批量加载到 B+Tree 索引中的速度更快。
  • 聚合(GROUP BY)。 

3 内存排序

如果内存足以容纳数据,那么我们可以使用标准排序算法,例如快速排序。

大多数数据库系统使用快速排序进行内存排序。
在其他数据平台中,尤其是 Python,默认排序算法是 TimSort。 它是插入排序和二元归并排序的结合。 通常在真实数据上效果很好。


如果内存不足以容纳数据,那么我们需要使用一种能够感知读取和写入磁盘页面成本的技术。

4 排序算法

今天我们主要讲下面几个算法:

  • Top-N 堆排序【Top-N Heap Sort】
  • 外部归并排序【External Merge Sort】
  • 聚合【Aggregations】

4.1 Top-N 堆排序【Top-N Heap Sort】

如果查询【Query】中包含带有 LIMIT 的 ORDER BY,则 DBMS 只需扫描数据一次即可找到前 N 个元素。


堆排序的理想场景:如果 topN 元素适合内存。

  • 扫描一次数据,在内存中维护一个排序的优先级队列。

1️⃣ 我们扫描数据

2️⃣ 并将其设置到堆排序的数组中 

3️⃣ 当我们扫描到 9 时,由于我们只取最小的 4 个元素,因此9直接在内存中被跳过

 

4️⃣ 而碰到元素1后,他会改变我们的堆排序数组

4.2 外部归并排序

分而治之算法,将数据分成单独的 runs ,分别对它们进行排序,然后将它们组合成更长的排序了的 runs。
第 1 阶段 – 排序

  • 对适合内存的数据块进行排序,然后将排序后的数据块写回到磁盘上的文件中。

第 2 阶段 – 合并

  • 将排序后的 runs 合并成更大的 chunks。 

run 是键/值对的列表。
key:用于比较以计算排序顺序的属性【attribute】。
value:有两种选择

  • 元组(早期的物化)。
  • 记录 ID(后期的物化)。

 4.2.1 2路外部归并排序

我们将从 2 路外部归并排序的简单示例开始。

  • 其中,“2”是我们每次合并的 run 的数量

数据被分成 N 页。


DBMS 有有限数量的 B 个缓冲池页来保存输入和输出数据。

栗子:

我们有 N 页的数据,而 DBMS 有一个 B 页大小的缓冲池来保存输入和输出数据。

Pass 0

  • 将表的所有 B 页【pages】读入内存
  • 对页【page】进行排序并将其写回磁盘

Page 2,3,,,

  • 递归地将一对 run 合并为两倍长的 run
  • 使用三个缓冲区页面(2 个用于输入页面,1 个用于输出页面)

在每次一次传递【Pass】中,我们都要读写每一个页。

总的传递【Pass】数:= 1 + ⌈ log2 N ⌉

总的 IO 花费 = 2N · (# of passes)


 

该算法仅需要三个缓冲池页面来执行排序(B=3)。

  • 两个输入页面,一个输出页面

但是,即使我们有更多可用 Buffer Pool 空间 (B>3),如果工作线程【worker】必须阻塞磁盘 I/O,那么依然无法有效利用它们。

双缓存优化

当系统处理当前 run 时,在后台预取下一次的 run 并将其存储在第二个缓冲区中。通过持续利用磁盘来减少每个步骤中 I/O 请求的等待时间。

通用外部归并排序 

Pass 0

  • 使用 B 个 缓冲页
  • 产生  ⌈N / B⌉ 个 B 大小的有序 run

Pass #1,2,3,…

  • 归并 B-1 个 runs (i.e., K-way merge)

总的传递【Pass】数:= 1 + ⌈ logB-1 ⌈N / B⌉ ⌉

总的 IO 花费 = 2N · (# of passes)

栗子:

确定使用大小为 5 页的 Buffer Pool 来排序占用 108 页的数据,需要经过几次传递【Pass】?

Pass #0: ⌈N / B⌉ = ⌈108 / 5⌉ = 22 个 5 页大小的排好序的 run

Pass #1: ⌈N’ / B-1⌉ = ⌈22 / 4⌉ = 6 个 20 页大小的排好序的 run(最后一个 run 只有3页)

Pass #2: ⌈N’’ / B-1⌉ = ⌈6 / 4⌉ = 2 个排好序的 run ,第一个有 80 页大小,第二个只有 28 页

Pass #3: Sorted file of 108 pages

1+⌈ logB-1⌈N / B⌉ ⌉ = 1+⌈log4 22⌉ = 1+⌈2.229...⌉ = 4 passes

比较优化

方法1:代码特化/硬编码:不要提供比较函数作为排序算法的指针,而是创建特定于键类型的硬编码版本的排序。

方法2:后缀截断:首先比较长 VARCHAR 键【key】的二进制前缀,而不是较慢的字符串比较。 如果前缀相等,则回退到较慢的版本。

4.3 使用 B 树进行排序

如果需要排序的表在排序属性上已经有 B+Tree 索引,那么我们可以使用它来加速排序。
只需遍历树的叶节点所在的页,即可按所需的排序顺序检索元组。
需要考虑下面两种情况:

  • 聚簇 B+树
  • 非聚集B+树
聚簇索引

遍历到最左边的叶节点的页,然后从所有叶节点的页中检索元组。
这种办法总是比外部归并排序更好,因为没有计算成本,并且所有磁盘访问都是顺序的。

非聚簇索引

这几乎总是一个坏主意。因为在非聚簇索引种,我们需要追踪指向包含数据的页面的每个指针。

一般来说,每个数据记录一个 I/O,这是随机 IO 的灾难。

5 聚合 

聚合是一种函数【function】,将多个元组中单个属性的值折叠为单个标量值【scalar value】。比如 min / max / sum / count /avg。

当我们要实现这些聚合时,我们需要找到一种方法,让DBMS系统快速找到与我们试图在其上构建聚合的属性具有相同值的元组,这样我们就可以对它们进行分组【grouping】计算我们想要的聚合函数。

两种实现选择:

  • 排序
  • 哈希,哈希的方案一般是要比排序更好的,尤其是当你的操盘速度很慢时(最小化IO)。

5.1 排序聚合

我们以一个栗子开始,这是我们的表:

我们要在该表上查询特定条件下不重复的 cid

SELECT DISTINCT cid

FROM enrolled

WHERE grade IN ('B','C')

ORDER BY cid

首先,我们遍历表,我们要应用 where 子句中的过滤条件,来找到所有成绩为 B 和 C 的元组

然后我们做一个映射【Projection】,因为我们不需要所有的属性,我们应用映射,只保留 cid

最后,我们对这个单列【column】进行应用排序,现在要生成最终结果,我们只需要扫描排序产生的输出,并跟踪我在光标处看到的最后一个值是什么,然后如果我遇到了和我之前看到的相同的值,那么我知道,它是重复的,我可以把它扔掉。

5.2 排序的替代方案 

如果我们不需要数据有序的话,排序可能并不是最好的方案,那么这该怎么办?

  • 在 GROUP BY 中形成组(无顺序)
  • 删除 DISTINCT 中的重复项(无顺序)

在这种情况下,哈希散列是更好的选择。

  • 只需要去重,无需排序。
  • 计算成本比排序更便宜。

5.3 哈希聚合

当 DBMS 扫描表时填充临时哈希表。 对于每条记录,检查哈希表中是否已有条目:

  • DISTINCT:丢弃重复项
  • GROUP BY:执行聚合计算

如果内存足够容纳着一切数据,那么这很容易。
如果 DBMS 必须将数据溢出到磁盘,那么我们需要变得更智能一点。

外部哈希聚合【External Hashing Aggregation】

第 1 阶段 – 分区【Partition】

  • 根据哈希键将元组【tuple】划分为桶【bucket】
  • 当它们已满时将它们写到磁盘

使用哈希函数 h1 将元组拆分为磁盘上的分区【Partition】。

  • 分区是包含具有相同哈希值的一组键的一个或多个页面,它是一种逻辑分组
  • 分区通过输出缓冲区“溢出”到磁盘

假设我们有 B 个缓冲区。 我们将使用 B-1 个缓冲区用于输出数据,即用于分区【Partition】,1 个缓冲区用于输入数据。

我们继续复用用排序聚合时的栗子:

我们首先对数据做条件过滤没然后只保留满足条件的元组:

然后通过映射【Projection】删除无用的列

我们对 cid 应用哈希函数 

阶段 #2 – ReHash

  • 为每个分区构建内存哈希表并计算聚合

对于磁盘上的每个分区:

  • 我们要将该分区的所有页面读入内存,逐一扫描它们,并基于第二个哈希函数 h2 构建内存中哈希表。
  • 然后遍历该内存哈希表的每个存储桶,以将匹配的元组组合在一起。

我们这里假设每个分区都适合内存大小。

我们继续基于第一阶段产出的分区,进行第二阶段的操作:

1️⃣ 首先我们对每一个分区应用哈希函数 h2,并写入专属于自己分区的哈希表中,需要注意的是,这个哈希表不是简单的原样存储,而是针对聚合函数的特定实现。

 2️⃣ 在将一个分区rehash完,我们再将内存哈希表中的每一个桶中的值,写入到最终结果集中

3️⃣ 在一个分区操作完之后,继续执行下一个分区,循环往复,直到所有分区都处理完。

在 ReHash 阶段,存储(GroupKey→RunningVal) 形式的序对【Pair】。

当我们想在哈希表中插入一条新的元组时:

  • 如果我们找到匹配的 GroupKey,我们只需要适当的更新RunningVal
  • 否则,插入一条 GroupKey→RunningVal

下面我们举个例子来解释一下,我们使用一个新的查询【Query】

SELECT cid, AVG(s.gpa)

FROM student AS s, enrolled AS e

WHERE s.sid = e.sid

GROUP BY cid

1️⃣ 我们先对数据集做分区

2️⃣ 然后应用哈希函数 h2 ,我们聚合函数要的是平均值,因此哈希表的值是关于聚合函数的特定值,它是数量和平均值的一个复合体

 3️⃣ 在rehash完,我们将哈希表写入结果集中

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

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

相关文章

新增长100人研讨会:20+上海医疗企业共探数字驱动下的目标管理与业绩增长策略

近日,纷享销客新增长100人系列活动之上海医疗专场,我们有幸邀请百趣生物一起,共同探讨医疗器械行业数字化增长的新理念、新方法和新实践。 活动聚集了百趣生物、汉维生物、松佰牙科器械、多宁生物、松佰医疗、瑞丰达医疗等20余位标杆医疗健康…

蓝桥杯简单模板

目录 最大公约数 两个数的最大公约数 多个数的最大公约数 最小公倍数 两个数的最小公倍数 多个数的最小公倍数 素数 ​编辑 位数分离 正写 ​编辑 反写 闰年 最大公约数 两个数的最大公约数 之前看见的是辗转相除法,例如现在让算一个49,21…

数码相框-LCD显示多行文字

显示几行文字: 从左显示:先描边再算出边框。居中显示:先算出边框,再确定坐标描画。 从左显示 第一行数据的起始位置是从(0,24)开始的。 要知道第二行数据从哪里开始,我们得知道画出来的矢量字体的边框是多少: 这个…

【C++】 详解 lower_bound 和 upper_bound 函数(看不懂来捶我!!!)

目录 一、前言 二、函数详解 🥝 lower_bound 🍍upper_bound 三、常考面试题 四、共勉 一、前言 这两个函数是我在 LeetCode 上做题见到,看到不熟悉的函数 lower_bound 和 upper_bound让我感觉很难受,于是在 C 官网去学习&…

2024HW --->反序列化漏洞!

对于反序列化,这个漏洞也是常用的,不过涉及到的方面非常非常广,比其他漏洞也难很多 于是本篇文章就分成PHP和JAVA的反序列化来讲讲 1.反序列化 想要理解反序列化,首先就要理解序列化 序列化:把对象转换为字节序列的过…

默克尔(Merkle)树 - 原理及用途

默克尔(Merkle)树的原理以及用途 引言 在当今数字化时代,确保数据的完整性是至关重要的。默克尔树作为一种高效的数据结构,被广泛应用于网络安全、分布式系统以及加密货币等领域,用于验证大量数据的完整性和一致性 数…

代码随想录算法训练营Day48|LC198 打家劫舍LC213 打家劫舍IILC337 打家劫舍III

一句话总结&#xff1a;前两题白给&#xff0c;第三题树形DP有点难。 原题链接&#xff1a;198 打家劫舍 滚动数组直接秒了。 class Solution {public int rob(int[] nums) {int n nums.length;int first 0, second nums[0];for (int i 2; i < n; i) {int tmp Math.m…

mega2560读取sick位移传感器

本次的项目中&#xff0c;需要使用到mega2560来读取sick位移传感器的模拟量&#xff0c;再把模拟量进行转换&#xff0c;从而使得到的数据为位移传感器的示数。 下面是位移传感器的接线图&#xff1a;棕色线接&#xff0b;24v&#xff0c;蓝色线接0v&#xff0c;白色线为模拟量…

JS 表单验证

点击注册的时候&#xff0c;渲染出来&#xff0c;验证码是自动获取出来的 html&#xff1a; <div class"div1">用户名<input type"text" id"yhm"><span id"span1"></span><br>密码<input type"…

mysql 查询变量@i:=@i+1

学习完mysql的查询&#xff1a;基本查询&#xff0c;连接查询和子查询和mysql 正则表达式查询&#xff0c;接下来先学习下变量查询。 mysql中没有oracle序列号那一列。mysql可以使用查询变量的方式去处理。我们先了解下查询变量&#xff0c;后面应用起来就更清晰。 1&#xff0…

【科东软件】鸿道Intewell-Lin_V2.2.0 软件版本发布

鸿道操作系统 Intewell-Lin_V2.2.0 软件版本发布 Intewell-Lin_V2.2.0 版本号&#xff1a;V2.2.0 版本或修改说明 增加功能&#xff1a; 1、增加T3板级支持 支持硬件列表

深入探索实时音视频技术:RTC程序设计权威指南

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

2024年32款数据分析工具分五大类总览

数据分析工具在现代商业和科学中扮演着不可或缺的角色&#xff0c;为组织和个人提供了深入洞察和明智决策的能力。这些工具不仅能够处理大规模的数据集&#xff0c;还能通过强大的分析和可视化功能揭示隐藏在数据背后的模式和趋势。数据分析工具软件主要可以划分为以下五个类别…

网络与通信-路由协议及基础配置

网络协议之路由协议 静态路由&#xff1a; 明细静态 默认静态 动态路由&#xff1a;&#xff08;可以自动去环&#xff09; RIP 十几台或几十台 &#xff08;维护上一代人搭建的网络&#xff09; OSPF 300台 &#xff08;最短路径算法&#xff09; ISIS 1200台 BGP…

深度学习500问——Chapter06: 循环神经网络(RNN)(2)

文章目录 6.4 CNN和RNN的区别 6.5 RNNs与FNNs有什么区别 6.6 RNNs训练和传统ANN训练异同点 6.7 为什么RNN训练的时候Loss波动很大 6.8 标准RNN前向输出流程 6.9 BPTT算法推导 6.9 RNN中为什么会出现梯度消失 6.10 如何解决RNN中的梯度消失问题 6.4 CNN和RNN的区别 类别特点描述…

主流三种驱动器方案特点简介

三种执行器原理相似&#xff0c;但在结构和部件上略有区别&#xff0c;因此在精度、响应速度等指标上 呈现不同效果&#xff1a; &#xff08;1&#xff09;TSA&#xff08;刚性驱动器&#xff09;&#xff1a;常规高速电机高传动比减速机高刚度力矩传感器&#xff0c;减 速机…

【Spring Cloud】服务容错中间件Sentinel入门

文章目录 什么是 SentinelSentinel 具有以下特征&#xff1a;Sentinel分为两个部分: 安装 Sentinel 控制台下载jar包&#xff0c;解压到文件夹启动控制台访问了解控制台的使用原理 微服务集成 Sentinel添加依赖增加配置测试用例编写启动程序 实现接口限流总结 欢迎来到阿Q社区 …

【QingHub】QingHub Studio企业级应用开发管理

QingHub Studio企业级应用开发设计器是QingHub Studio的一个核心模块&#xff0c;它可以实现应用搭建、团队管理&#xff0c;共享开发&#xff0c;可以快速接入API接口&#xff0c;复杂功能可以通过自定义脚本快速实现业务逻辑。打通前端开发与后台业务逻辑一体化。通过可视化的…

Vue - 2( 10000 字 Vue 入门级教程)

一&#xff1a;初识 Vue 1.1 绑定样式 1.1.1 绑定 class 样式 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>绑定样式</title><style>......</style><script type"text/javascript"…

【六 (3)机器学习-机器学习建模步骤/kaggle房价回归实战】

目录 文章导航一、确定问题和目标&#xff1a;1、业务需求分析&#xff1a;2、问题定义&#xff1a;3、目标设定&#xff1a;4、数据可行性评估&#xff1a;5、资源评估&#xff1a;6、风险评估&#xff1a; 二、数据收集&#xff1a;1、明确数据需求2、选择数据来源3、考虑数据…