【数据库内核】物理执行器引擎Pull模型之火山模型

2023-11-06

目录

概述

算子介绍

火山模型

火山模型痛点

一、虚函数的开销

二、Cpu Cache的利用率

结论

参考资料


概述

 

当我们的逻辑计划引擎把SQL生成了逻辑计划后,后端的物理计算引擎接收到逻辑计划生成物理执行计划,便可以开始去真正执行计算作业了。在关系数据库发展的早期,受制于计算机IO能力的约束,数据库通常的物理执行引擎采用的是火山模型的设计方式。因为火山模型每次只处理一行数据,大大的节约了内存的使用量。

数据库物理计算引擎通常分为二类,一类是以火山模型(Volcano Model)为代表的拉取模型。另一类是以Pipeline为代表的推模型。本文主要讲述的是第一种火山模型(Volcano Model)。了解火山模型前,我们了解一下数据库的一些基本算子。

 

算子介绍

 

先简单介绍数据库中基本的算子。

  • Scan算子,作用是读取数据源的具体数据。例如: select * from t 就是读取t表中的数据。
  • Selection算子,作用是处理Sql中的谓词条件。例如: select xxx from t where xx = 5 里面的 where 过滤条件就是Selection算子处理的。
  • Projection算子,作用是物理执行器计算出的数据传递给外部。例如: select c from t 中 Projection算子是输出C列中的数据。
  • Join算子,作用是计算两个表的连接后的数据。例如: select xx from t1, t2 where t1.c = t2.c 就是把 t1 t2 两个表做 Join。

 

 

 

火山模型

 

火山模型又叫迭代器模型, 最早于1990年Goetz Graefe在Volcano, an Extensible and Parallel Query Evaluation System这篇论文中提出(参考资料中有论文链接),在90年代早期,计算机的内存资源十分昂贵,相对于CPU的执行效率,IO效率要差得多,因此运算符和存储之间的IO交换是影响查询效率的主要因素(IO墙效应),火山模型将更多的内存资源用于IO的缓存设计而没有优化CPU的执行效率是在当时的硬件基础上很自然的权衡。

火山模型其基本思路十分简单:将关系代数当中的每一个算子抽象成一个迭代器。每个迭代器都带有一个 next 方法。每次调用这个方法将会返回这个算子的产生的一行数据(或者说一个 Tuple)。程序通过在 SQL 的计算树的根节点不断地调用 next方法来获得整个查询的全部结果。比如 SELECT col1 FROM table1 WHERE col2 > 0;这条 SQL 就可以被翻译成由三个算子组成的计算树。如下图所示,我们也可以使用伪代码将这三个算子的执行逻辑表示出来。

 

通过上图可以看到,Volcano 模型是十分简单的,而且他对每个算子的接口都进行了一致性的封装。也就是说,从父节点来看,子节点具体是什么类型的算子并不重要,只需要能源源不断地从子节点的算子中 Fetch 到数据行就可以。

上面的例子简单的说明了下火山模型的工作原理,下面我们举一个稍微复杂点的例子了解一下物化的这个概念,有一个 JOIN 算子,因此在生成的执行方案树当中会有一个分叉。我们假定 RIGHT_TABLE 表的行数远小于 LEFT_TABLE 表,这样一个合理的执行方案可能是先将 RIGHT_TABLE 表的所有数据读出来构造一个 Hash 表,再将 LEFT_TABLE 表当中的每一行读出并通过 Hash 表查询获得结果。此时的 Volcano 模型的执行要更复杂些。JOIN 算子为了向上封装这些细节,需要在内部不断调用 RIGHT_TABLE 表的 next 方法直到将其内容全部读取出来。

 

SQL如下:

SELECT  LT.C1,  LT.C2,  RT.C2 FROM  LEFT_TABLE LT  JOIN RIGHT_TABLE RT ON LT.C1 = RT.C1;

 

因此,对于这样一条 SQL,他的执行树表示成下图的样子。

 

如上图所示,我们将 RIGHT_TABLE 的内容读入 Hash 表,构建 RIGHT_TABLE.C1 -> RIGHT_TABLE.C2 的映射。之后再逐一读入 LEFT_TABLE 表的内容,对于 LEFT_TABLE 表的每一行,我们使用它的 C1 列在 Hash 表中进行查询,从而得到 JOIN 之后的结果。

JOIN 算子的执行分为两个部分。第一个部分它会在内部不断调用 RIGHT_TABLE.next() 函数构建 Hash 表,之后调用 LEFT_TABLE 的 SCAN 算子一次以便返回第一行结果。之后对 JOIN 的调用就只会读取 LEFT_TABLE 的内容进行并在 Hash 表里探查(Probe)。这一方式实现的 Hash 表是一个带有内部状态的算子,与其他的算子大不相同。而它构建内部 Hash 表的过程可以被认为是一种物化(Materialization)。

 

火山模型痛点

 

火山模型简单明了,也非常灵活。但是在当代CPU的硬件环境与大数据场景下,性能表现却差强人意。究其原因。主要有如下几点:

 

一、虚函数的开销

next函数通常是一次虚函数调用。在Java中,为了支持多态,所有的非静态方法与非final方法本质上都是动态绑定的虚函数,在JVM层依赖于invokevirtual和invokeinterface这两个方法指令。虚函数调用与普通函数的调用的区别在于后者是一次直接调用,直接调用的跳转地址在编译时是确定的。而虚函数调用是一次间接调用,需要在运行时才能从虚表获取地址再跳转。所以,虚函数首先会多一次寻址的时间开销;其次,虚函数是无法在编译期做内联优化的,所以运行时会实实在在多一次函数调用开销。对于Java来说,方法调用会在运行时会多一次栈桢入栈出栈操作;最后,也是最重要的一点,虚函数的间接调用本身会导致分支预测。据统计,间接分支预测的失败率在25%左右。一旦分支预测失败,将会导致流水线被冲刷,进而需要重新获取指令、译码,对性能造成严重的影响。

 

二、Cpu Cache的利用率

这种风格对代码和数据的局部性并不友好。它不能很好的利用CPU的缓存机制。由于CPU的各级Cache都是从下一级存储按特定长度单位对齐取数的,且取到的数据也是连续的,所以每次取到的连续的数据如果能被集中充分处理,将会得到最大的收益。但next调用每次只处理一条记录,而且火山模型是个拉取的模型,即下游什么时候拉取并不随当前算子控制,所以当前算子刚刚cache的数据在只取用了一部分就很有可能马上被其他算子的next调用获取的数据给冲掉(evict)了。而当前算子再次调用next时又会冲掉别人的cache,重新将之前被人冲掉的未处理的数据从较慢的存储中读取来给CPU处理。这样的周而复始,造成了不必要的开销。而且拉取模型也要求严格以算子为界进行物化,其实很多物化点是可以避免的。

大数据时代的体量放大了1和2的问题。如果数据量不多,那么1和2的问题并不明显。但是在大数据时代,我们需要处理的数据量从百万到数十亿是家常便饭。每条记录都会经历next的调用,所以性能问题将被放大百万倍、数十亿倍以至更甚。从而警醒我们不容忽视。

那么有没有一种解决方案处理虚函数的问题呢?火山模型影响的是执行流程的代码布局,但只要是解释执行,就无法避免运算符之间的虚函数调用。随着计算机硬件的发展,内存变得越来越大,这意味着越来越多的数据可以直接cache在内存中,而访问磁盘的频率被大幅度的降低,这个时候“IO墙”的效应被削弱,而由于解释执行无法感知CPU寄存器,高频的内存访问,使CPU和内存之间形成了“内存墙”效应,为了解决这个问题,越来越多的内存数据库开始使用Llvm编译执行的技术来优化自己的查询效率,例如HyPer、MemSQL、Hekaton、Impala、Spark Tungsten等,参考资料中给出了impala中的使用编译执行的论文。

那么有没有一种解决方案处理CPU Cache的问题呢?答案是显而易见的,我们可以通过批量处理的方式来优化处理。之前提到,在火山模型中,next方法是一次处理一条记录,而我们知道next的调用开销是挺高的,而一次处理一条记录对于代码和数据的局部性都不是很友好。所以,如果能next方法改善为例如nextBatch方法,让每次能在算子里一次性处理的数据与CPU的Cache Line对齐,那将在局部性这块上得到最大化的收益。当然,batch不宜过大,正好能放入Cache Line为佳。批处理计算还为进一步的优化(如 SIMD 指令执行)提供了有利条件。

 

结论

 

本文介绍了物理计算引擎的Pull模型是实现之一的火山模型。火山模型是一种简单的计算模型,把各个的物理算子抽象成一个个迭代器。每一个算子只关心自己内部的逻辑即可,让各个算子之间的耦合性降低。但是同样火山模型也带来的虚函数开销和CPU Cache不友好的问题。下一节中带来物理计算引擎Push模型的Pipeline设计方式。敬请期待。

 

参考资料

  1. https://paperhub.s3.amazonaws.com/dace52a42c07f7f8348b08dc2b186061.pdf
  2. http://sites.computer.org/debull/A14mar/p31.pdf

 

分享大数据行业的一些前沿技术和手撕一些开源库的源代码
微信公众号名称:技术茶馆
微信公众号ID    :    Night_ZW
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【数据库内核】物理执行器引擎Pull模型之火山模型 的相关文章

随机推荐

  • auto_copy.py

    coding utf 8 author DT date 20190118 import os import os path import shutil import time datetime def del file path for f
  • k8s1.23.15版本二进制部署/扩容及高可用架构详解

    前言 众所周知 kubernetes在2020年的1 20版本时就提出要移除docker 这次官方消息表明在1 24版本中彻底移除了dockershim 即移除docker 但是在1 24之前的版本中还是可以正常使用docker的 考虑到可
  • kali虚拟机安装出现无法定位软件包,无法找到任何软件包的解决方法

    问题如下 出现这种安装问题 原因是长时间未打开kali虚拟机 解决方法 输入命令 sudo apt update 注 在root或非root用户下都可以 然后继续安装就可以了
  • 【NLP】第 8 章:使用基于注意力的神经网络构建聊天机器人

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • python爬虫正则表达式

    博主简介 博主是一个大二学生 主攻人工智能领域研究 感谢缘分让我们在CSDN相遇 博主致力于在这里分享关于人工智能 C python 爬虫等方面的知识分享 如果有需要的小伙伴 可以关注博主 博主会继续更新的 如果有错误之处 大家可以指正 专
  • 蓝桥杯常用模板

    文章目录 蓝桥杯算法模板 最大公约数 GCD 拓展欧几里得 EXGCD 最小公倍数 LCM 多个数的GCD和LCM 判断闰年 整数快速幂 用于快速求幂 快速幂取模 快速乘 O logn 对于一个大数最后结果只需要部分结尾数字 则在过程中取模
  • (十一)javascript内置对象之Math内置对象

    内置对象就是 js 语言自带的一些对象 这些对象可供开发者使用 这些内置对象提供了一些常用的或是最基本而必要的一些功能 内置对象最大的优点是帮助我们快速开发 javascript提供的常用的内置对象有 Math Date Array Str
  • matlab plot 连续曲线,Matlab怎么画出连续的曲线?

    因为你是在for循环中画的 所以循环一次算出一个点 matlab就画一个点 你可以在循环完毕后在使用plot画图 clear all clc i 1 脚标i L1 1 L2 1 L 1 C1 1 C2 1 C 1 m 0 5 w 50 a
  • C++期末复习运算符混淆---打妖怪

    7 2 打妖怪 低级错误 运算符混淆问题 实验1 第二题 话说孙大圣保唐僧西天取经 路上遇到一妖怪 妖怪共有 v 滴血 大圣每打一棒就能使妖怪失去 h 滴血 妖怪一旦没血就会立即死去 大圣打 n 棒刚好将妖怪打死 请编写程序 输入 v 和
  • 代码题: 看代码说结果, 事件循环 + async 函数的

    1 基本的 async await 和事件循环 console log 1 async function asyncFunc console log 2 await Promise resolve console log 3 asyncFu
  • Simulated Binary Crossover(SBX)的学习

    最近在做作业遇到一个Dejong s fifth function的multi modal的问题 用传统的GA方法尝试了很多次 的确没办法搞定 随机很多次也不一定在global optimum的地方得到一次解 前几天去导师家里的路上谈到这个
  • 什么是 MATLAB(矩阵实验室)?工作、功能和应用

    MATLAB 被 MathWorks 定义为专有软件应用程序和编程语言 可促进复杂的数据分析任务 例如算法实施 与其他应用程序交互以及操作数据矩阵 本文介绍了 MATLAB 的用途 其关键概念以及 2022 年的用例 什么是 MATLAB
  • 【解决】TypeError: this.$refs.treeRefs.xxx is not a function

    问题 使用refs触发子组件内方法时报 TypeError this refs treeRefs xxx is not a function 解决 经查看 发现子组件被放在了v for循环体内 示例代码如下
  • 虚拟机上部署K8S集群

    虚拟机上部署K8S集群 安装VM Ware 安装Docker 安装K8S集群 安装kubeadm 使用kubeadm引导集群 安装VM Ware 参考 http www taodudu cc news show 2034573 html a
  • 推荐系统:Wide & Deep模型解析

    1 Wide Deep模型介绍 经典推荐深度模型 Wide Deep 对应的论文 Wide Deep Learning for Recommender Systems 链接 arxiv Wide Deep的模型架构如下图所示 可以看到Wid
  • 运维技能风向标

    运维介绍 运维是一个融合多学科 网络 系统 开发 安全 应用架构 存储等 的综合性技术岗位 从最初的网络管理 网管 发展到现在的系统运维工程师 网络运维工程师 安全运维工程师 运维开发工程师等 可以看出 运维的分工一直在细化 并且对综合技能
  • Element Plus for Vue 3 入门教程

    本文首发 Element Plus for Vue 3 入门教程 Element Plus 有那些升级 Element Plus 与 Element UI 是什么关系 老 Element 项目是否可以平滑升级到 Vue 3 Element
  • 如何快速实现Android平台前端设备接入能力

    技术背景 SIP 会话初始化协议 是在 IP网络上进行多媒体通信的应用层控制协议 以几种RFC的形式提供 其中最重要的是包含核心协议规范的RFC3261 该协议用于创建 修改和终止与一个或多个参与者的会话 通过会话 我们了解了一组进行通信的
  • linux inode满了后,怎么清理

    最近服务器的inode inode介绍 达到了90 当 Ised 节点使用率 达到100 时 即使文件系统有剩余空间也无法写入数据 这里记录下解决方法 清理文件系统下 细碎文件 施放节点数 因为该路径下的文件都是重要文件 不能够删除 所以这
  • 【数据库内核】物理执行器引擎Pull模型之火山模型

    目录 概述 算子介绍 火山模型 火山模型痛点 一 虚函数的开销 二 Cpu Cache的利用率 结论 参考资料 概述 当我们的逻辑计划引擎把SQL生成了逻辑计划后 后端的物理计算引擎接收到逻辑计划生成物理执行计划 便可以开始去真正执行计算作