当前位置: 首页 > news >正文

第八讲 Sort Aggregate 算法

我们现在将讨论如何使用迄今为止讨论过的 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 的灾难。

4.4 聚合 

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

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

两种实现选择:

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

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

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

SELECT DISTINCT cid

FROM enrolled

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

ORDER BY cid

 

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

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

 

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

4.4.2 排序的替代方案 

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

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

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

  • 只需要去重,无需排序。
  • 计算成本比排序更便宜。
4.4.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完,我们将哈希表写入结果集中

相关文章:

第八讲 Sort Aggregate 算法

我们现在将讨论如何使用迄今为止讨论过的 DBMS 组件来执行查询。 1 查询计划【Query Plan】 我们首先来看当一个查询【Query】被解析【Parsed】后会发生什么? 当 SQL 查询被提供给数据库执行引擎,它将通过语法解析器进行检查,然后它会被转换…...

clickhouse MPPDB数据库--新特性使用示例

clickhouse 新特性: 从clickhouse 22.3至最新的版本24.3.2.23,clickhouse在快速发展中,每个版本都增加了一些新的特性,在数据写入、查询方面都有性能加速。 本文根据clickhouse blog中的clickhouse release blog中,学…...

MATLAB多级分组绘图及图例等细节处理 ; MATLAB画图横轴时间纵轴数值按照不同sensorCode分组画不同sensorCode的曲线

平时研究需要大量的绘图Excel有时候又臃肿且麻烦 尤其是当处理大量数据时可能会拖死Windows 示例代码及数据量展示 因为数据量是万级别的折线图也变成"柱状图"了, 不过还能看出大致趋势! 横轴是时间纵轴是传感器数值图例是传感器所在深度 % data readtable(C:\U…...

20240405,数据类型,运算符,程序流程结构

是我深夜爆炸&#xff0c;不能再去补救C了&#xff0c;真的来不及了&#xff0c;不能再三天打鱼两天晒网了&#xff0c;真的来不及了呜呜呜呜 我实在是不知道看什么课&#xff0c;那黑马吧……MOOC的北邮的C正在进行呜呜 #include <iostream> using namespace std; int…...

Prometheus+grafana环境搭建Nginx(docker+二进制两种方式安装)(六)

由于所有组件写一篇幅过长&#xff0c;所以每个组件分一篇方便查看&#xff0c;前五篇链接如下 Prometheusgrafana环境搭建方法及流程两种方式(docker和源码包)(一)-CSDN博客 Prometheusgrafana环境搭建rabbitmq(docker二进制两种方式安装)(二)-CSDN博客 Prometheusgrafana环…...

贝叶斯逻辑回归

贝叶斯逻辑回归&#xff08;Bayesian Logistic Regression&#xff09;是一种机器学习算法&#xff0c;用于解决分类问题。它基于贝叶斯定理&#xff0c;通过建立一个逻辑回归模型&#xff0c;结合先验概率和后验概率&#xff0c;对数据进行分类。 贝叶斯逻辑回归的基本原理是…...

Win10 下 Vision Mamba(Vim-main)的环境配置(libcuda.so文件无法找到,windows系统运行失败)

目录 1、下载NVIDIA 驱动程序、cuda11.8、cudnn8.6.0 2、在Anaconda中创建环境并激活 3、下载gpu版本的torch 4、配置环境所需要的包 5、安装causal_conv1d和mamba-1p1p1 安装causal_conv1d 安装mamba-1p1p1 6、运行main.py失败 请直接拉到最后查看运行失败的原因&am…...

4 万字全面掌握数据库、数据仓库、数据集市、数据湖、数据中台

如今&#xff0c;随着诸如互联网以及物联网等技术的不断发展&#xff0c;越来越多的数据被生产出来-据统计&#xff0c;每天大约有超过2.5亿亿字节的各种各样数据产生。这些数据需要被存储起来并且能够被方便的分析和利用。 随着大数据技术的不断更新和迭代&#xff0c;数据管…...

Leetcode 64. 最小路径和

心路历程&#xff1a; 第一反应像是一个回溯问题&#xff0c;但是看到题目中要求最值&#xff0c;大概率是一道DP问题。并且这里面的递推关系也很明显。 这里面边界条件可以有多种处理方法。 解法&#xff1a;动态规划 class Solution:def minPathSum(self, grid: List[List…...

FANUC机器人故障诊断—报警代码更新(三)

FANUC机器人故障诊断中&#xff0c;有些报警代码&#xff0c;继续更新如下。 一、报警代码&#xff08;SRVO-348&#xff09; SRVO-348DCS MCC关闭报警a&#xff0c;b [原因]向电磁接触器发出了关闭指令&#xff0c;而电磁接触器尚未关闭。 [对策] 1.当急停单元上连接了CRMA…...

mysql 本地电脑服务部署

前提&#xff1a; 下载mysql 新建配置文档 在安装mysql目录新建 my.ini [mysqld] # 设置3306端口 port3306#设置mysql的安装目录 basedirC:\Program Files\MySQL\MySQL Server 8.3 #切记此处一定要用双斜杠\\,单斜杠我这里会出错&#xff0c;不过看别人的教程&#xff0c;有…...

爬虫学习第一天

爬虫-1 爬虫学习第一天1、什么是爬虫2、爬虫的工作原理3、爬虫核心4、爬虫的合法性5、爬虫框架6、爬虫的挑战7、难点8、反爬手段8.1、Robots协议8.2、检查 User-Agent8.3、ip限制8.4、SESSION访问限制8.5、验证码8.6、数据动态加载8.7、数据加密-使用加密算法 9、用python学习爬…...

labview如何创建2D多曲线XY图和3D图

1如何使用labview创建2D多曲线图 使用“索引与捆绑簇数组”函数将多个一维数组捆绑成一个簇的数组&#xff0c;然后将结果赋值给XY图&#xff0c;这样一个多曲线XY图就生成了。也可以自己去手动索引&#xff0c;手动捆绑并生成数组&#xff0c;结果是一样的 2.如何创建3D图 在…...

【华为OD机试】芯片资源限制(贪心算法—JavaPythonC++JS实现)

本文收录于专栏:算法之翼 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-芯片资源限制二.解题思路三.题解代码Python题解代码JAVA题解代码C/C++题解代码JS题解代码四…...

服务器硬件构成与性能要点:CPU、内存、硬盘、RAID、网络接口卡等关键组件的基础知识总结

文章目录 服务器硬件基础知识CPU&#xff08;中央处理器&#xff09;内存&#xff08;RAM&#xff09;硬盘RAID&#xff08;磁盘阵列&#xff09;网络接口卡&#xff08;NIC&#xff09;电源散热器主板显卡光驱 服务器硬件基础知识 服务器是一种高性能计算机&#xff0c;用于在…...

STC89C51学习笔记(四)

STC89C51学习笔记&#xff08;四&#xff09; 综述&#xff1a;本文讲述了在STC89C51中数码管、模块化编程、LCD1602的使用。 一、数码管 1.数码管显示原理 位选&#xff1a;对74HC138芯片的输入端的配置&#xff08;P22、P23、P24&#xff09;&#xff0c;来选择实现位选&…...

Arcgis Pro地理配准

目录 一、目的 二、配准 1、找到配准工具 2、添加控制点 3、选择控制点 4、添加更多控制点 5、配准完成、保存 三、附录 1、查看控制点或删除控制点 2、效果不好怎么办 一、目的 下面我们将两张地图进行配准&#xff0c;其中一张有地理位置&#xff0c;而另外一张没…...

数字转型新动力,开源创新赋能数字经济高质量发展

应开放原子开源基金会的邀请&#xff0c;软通动力董事、鸿湖万联董事长黄颖基于对软通动力开源战略的思考&#xff0c;为本次专题撰文——数字转型新动力&#xff0c;开源创新赋能数字经济高质量发展。本文首发于2023年12月12日《中国电子报》“开源发展与开发者”专题第8版。以…...

解决JavaWeb中IDEA2023新版本无法创建Servlet的问题

出现问题&#xff1a;IDEA右键创建Servlet时&#xff0c;找不到选项 原因分析&#xff1a;IDEA的2023版的已经不支持Servlet了&#xff0c;如果还要使用的话&#xff0c;需要自己创建模板使用 创建模板 右击设置&#xff0c;选择&#xff08;File and Code Templates&#x…...

关于oracle切换mysql8总结

最近由于项目换库&#xff0c;特此记录 1.字段类型 number(8) -> int(8) number(16) -> bigint(16) varchar2() -> varchar() 2.导数据 从oracle迁移数据到mysql&#xff0c;除了用专门的数据泵&#xff0c;经常需要用csv导入到mysql&#xff1b; 导出的csv数据如果…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...