技术贴 | SQL 执行 - 执行器优化

news2024/11/19 21:21:58

本期技术贴主要介绍查询执行引擎的优化。查询执行引擎负责将 SQL 优化器生成的执行计划进行解释,通过任务调度执行从存储引擎里面把数据读取出来,计算出结果集,然后返回给客户。

在关系型数据库发展的早期,受制于计算机 IO 能力的约束,计算在查询整体的耗时占比并不明显,这个时候关注重点主要放在对于查询的优化。优化器的好坏,对于执行计划的优劣有着重要的意义,查询执行引擎的作用在数据库优化中对应等级是相对弱化的。但随着计算机硬件的发展,查询执行引擎也逐渐展现出他们的重要地位。

本篇博客结合 KaiwuDB 的部分源码和实例,介绍其如何充分发挥底层硬件的能力,优化查询执行引擎,从而提升数据库系统的性能。查询执行引擎是否高效与其采用的模型有直接关系,1990年,论文"Volcano, an Extensible and Parallel Query Evaluation System"提出了火山模型,这也是 KaiwuDB 查询执行引擎的基础。

一、火山模型/迭代模型

火山模型作为经典的查询执行模型被诸如 Oracle、MySQL 等主流关系数据库采用。该模型将关系代数中每一种算子抽象为一个 Operator(迭代器),每个 Operator 都提供一个接口 Next(),调用该接口会返回该算子产生/处理的一行数据(Tuple)。通过在查询树根节点自顶向下地调用 Next(),数据自底向上地被拉取处理,因而火山模型也称为拉取执行模型(Pull Based)。

图片火山模型

以一个两表连接的查询为例,让我们留意图中的第 ④ 步,Select 运算符。

调用 Next() 方法从其子运算符请求下一行,并检查它是否通过了筛选条件。如果是,则该行将返回到其父运算符;否则,将丢弃该行并重复该过程。

// RunFilter runs a filter  expression and returns whether the filter passes.
func RunFilter(filter tree.TypedExpr, evalCtx *tree.EvalContext)(bool, error){
    if filter == nil{
        return true, nil
    }
    
    d, err := filter.Eval(evalCtx)
    if err != nil{
        return false,err
    }

    return d == tree.DBoolTrue, nil
}

上述即 KaiwuDB 在处理行时进行过滤的函数,参数 Filter 类型为 tree.TypedExpr,意为一个通用表达式。也就是说,对于每一行,都会调用一个完全通用的标量表达式的过滤器。表达式可以是任何东西:乘法、除法、相等检查或内置函数,它甚至可以是由上述表达式组成的树。由于这种通用性,计算机在过滤每一行时都有很多工作要做,它必须在做任何工作之前检查表达式是什么,这与解释型语言的逻辑相同(与编译型语言相比)。

尽管火山模型简单、直观、易用,只需将 Operator 自由地组装,且每个 Operator 只关心自己的处理逻辑,执行引擎并不感知。但是,在执行过程中,迭代一次只处理一行数据,数据局部性差,很容易使 CPU cache 失效,并且调用 Next() 函数(虚函数)次数太多,开销较大,使得 CPU 执行效率不高。

二、算子融合

将经常出现的 Operator(如 Project 和 Filter)融合在其他 Operator 中能够一定程度上减少虚函数的调用,提高单个 Operator 的处理能力和数据局部性。以 KaiwuDB 的 tablereader 算子为例,在扫表时便能够对数据行进行过滤和投影,其 Next() 函数中实现了相关逻辑。

下图是 tablereader 算子 Next() 函数调用的简化时序图,可以看到,在读取一条数据进行处理时会判断 Filter 和 outputCols 决定是否进行 Filter 和 Projection 操作。
图片TableReader 算子 Next() 函数调用的简化时序图

下图展示了一条查询语句示例的物理计划,也可以看到在 TableReader 算子中对范围 Spans 和输出列 Out 进行了限制。
查询的计划示例
查询的计划示例

三、向量化模型

不同于火山模型按行迭代的方式,如下图所示,向量化模型采用批量迭代,在算子间一次传递一批数据。通过更改数据方向(从行到列),把从列到元组的转化推迟到较晚的时候执行,来更有效地利用现代 CPU。连续的数据有利于 CPU cache 的命中,减少 memory stall 现象;除此之外,通过 SIMD 指令一次处理多个数据,可以充分利用 CPU 的计算能力。
火山模型和向量化模型迭代数据的差异
向量化模型整体架构与火山模型类似,依然采用了拉取式模型。考虑一个具有 Id,Name 和 Age 三列的表 People, batch 将由 Id 的整型数组、Name 的字符串数组以及 Age 的整型数组组成,面对查询 SELECT Name, (Age - 30) * 50 AS Bonus FROM People WHERE Age > 30; 其向量化模型大致下图所示。
向量化模型
显然向量化模型与列式存储搭配使用可以获得更好的效果,但非列式存储也可以采用折中的方式来实现向量化模型。KaiwuDB 使用的是行存储引擎,在其向量化执行模型中,在底层 Operator 中实现了多行到向量块的转化,上层的 Operator 则以向量块作为输入进行处理,最后再由顶层的 Operator 进行向量块到行数据的转化。

除此之外,为了避免上文提到的火山模型下由于 Filter 所使用标量表达式的通用性带来的额外计算开销,在 KaiwuDB 的向量化执行模型中,每个向量化 Operator 在执行期间不允许任何自由度或运行时选择。

这意味着,对于数据类型、属性和工作任务的任意组合,都有一个专门的 Operator 负责工作。执行引擎从 Operator 链请求 batch:每个 Operator 从其子级 Operator 请求一个 batch,执行其特定工作任务,然后将 batch 返回到其父级 Operator。

因此对于示例查询 SELECT Name, (Age - 30) * 50 AS Bonus FROM People WHERE Age > 30; 实际向量化模型比上述的内容更复杂,具体如下图所示。
具体向量化模型
SelectIntGreaterThanInt Operator 在获取 People 表的 batch 后将选择所有 Age 大于 30 的值;然后,这个新的 sel_age batch 将传递给 ProjectSubIntInt Operator,该 Operator 执行简单的减法以生成 tmp batch;最后,这个 tmp batch 被传递给 ProjectMultIntInt Operator,该 Operator 计算最终的 Bonus=(Age - 30)* 50。

为了具体实现这些向量化 Operator,KaiwuDB 将流程分解为单个列上的紧密 for 循环。以下的代码段(有删减)实现了 SelectIntGreaterThanInt Operator 的部分功能。该函数从其子项 Operator 中检索 batch,并循环访问列的每个元素,同时将大于 30(p.constArg) 的值选中标记。然后,将 batch 及其选中向量返回给父级 Operator 进行进一步处理。这段代码虽然简单但却非常有效,for 循环迭代了一个 int64 的切片,将每个切片元素与另一个 int64 常量进行比较,并将结果存储在另一个 int32 切片中,从而实现了一个快速的循环。


func (p *selGTInt64Int64Const0p) Next(ctx context,Context) coldata,Batch {
    // In order to inline the templated code of overloads, we need to have a
    // 'decimalScratch' local variable of type 'decimalOverloadScratch'.
    decimalScratch := p.decimalScratch
    // However, the scratch is not used in all of the selection operators, so
    // we add this to go around "unused" error.
    _ = decimalScratch
    for{
        batch := p.input.Next(ctx)
        if batch.Length() == 0 {
          return batch
    }
    vec := batch.ColVec(p.colIdx)
    col := vec.Int64()
    var idx int
    n := batch.Length()
    if sel := batch,Selection(); sel != nil {
      sel = sel[:n]
      for _, i := range sel {
        var cmp bool
        arg := col[i]
        {
          var cmpResult int
          {
            a, b := int64(arg), int64(p.constArg)
            if a < b {
              cmpResult = -1
            } else if a > b {
              cmpResult = 1 
            } else {
              cmpResult = 0
            }
          }
          cmp = cmpResult > 0
        }
        isNull := false
        if cmp && !isNull{
          sel[idx] = i
          idx++
        }
      }
    }
    if idx > 0 {
      batch.SetLength(idx)
      return batch
    }
  }
}

KaiwuDB 使用 kv 存储引擎 rocksdb 作为底层存储。通过从存储读取行后将行转换为列式数据的 batch,然后将这些 batch 送到向量化执行引擎中处理,对于处理大量数据时有可观的性能提升,但在数据量小时向量化是没有优势的,因为向量化的过程会带来额外的开销。

因此,面向行的执行模型可以为联机事务处理(OLTP)查询提供良好的性能,而向量化执行模式往往更适用于涉及海量数据的联机分析处理(OLAP)查询。KaiwuDB 在执行计划时会根据估计的 tablereader 输出的最大行数,与 SessionData 中的 VectorizeRowCountThreshold 字段比较来判断是否需要向量化执行。

KaiwuDB 默认开启向量化执行引擎,用户也可以选择关闭。关闭和开启向量化执行引擎可以通过 SET 进行设置,如下图所示。除此之外,使用 EXPLAIN(VEC)语句可用于查看查询的向量化执行计划。
通过 SET 设置关闭向量化执行引擎

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

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

相关文章

SLF4J: Class path contains multiple SLF4J bindings.

问题截图 问题原因 这里就是由于hbase 安装路径下的一个文件和hadoop安装路径下的文件起冲突了 解决办法 我的路径&#xff1a; 这个一定要看自己电脑上的路径 /usr/local/hbase/lib/client-facing-thirdparty/ slf4j-log4j12-1.7.25.jar更名为&#xff1a; /usr/local/hb…

手把手带你学习 JavaScript 的 ES6 ~ ESn

文章目录 一、引言二、了解 ES6~ESn 的新特性三、掌握 ES6~ESn 的用法和实现原理四、深入挖掘和拓展《深入理解现代JavaScript》编辑推荐内容简介作者简介精彩书评目录 一、引言 JavaScript 是一种广泛使用的网络编程语言&#xff0c;它在前端开发中扮演着重要角色。随着时间的…

Python | 机器学习之聚类算法

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《人工智能奇遇记》&#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 目录结构 1. 机器学习之聚类算法概念 1.1 机器学习 1.2 聚类算法 2. 聚类算法 2.1 实验目的…

基于Vue+SpringBoot的天然气工程业务管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目详细录屏 二、功能模块三、使用角色3.1 施工人员3.2 管理员 四、数据库设计4.1 用户表4.2 分公司表4.3 角色表4.4 数据字典表4.5 工程项目表4.6 使用材料表4.7 使用材料领用表4.8 整体E-R图 五、系统展示六、核心代码6.1 查询工程项目6.2 工程…

MAT工具定位分析Java堆内存泄漏问题方法

原创/朱季谦 一、MAT概述与安装 MAT&#xff0c;全称Memory Analysis Tools&#xff0c;是一款分析Java堆内存的工具&#xff0c;可以快速定位到堆内泄漏问题。该工具提供了两种使用方式&#xff0c;一种是插件版&#xff0c;可以安装到Eclipse使用&#xff0c;另一种是独立版…

Kibana:作为非设计师设计直观的 Kibana 仪表板

作者&#xff1a;Carly Richmond, Marco Vettorello, Giovanni Magni 开发人员、SRE 工程师和才华横溢的技术人员通常需要构建快速仪表板来展示有关其应用程序状态的重要信息&#xff0c;这些信息可供混合受众使用。 如果你不是前端开发人员或设计师&#xff0c;那么构建所有人…

【Mysql】MySQL基于成本的优化

什么是成本 我们之前说 过MySQL 执行一个查询可以有不同的执行方案&#xff0c;它会选择其中成本最低&#xff0c;或者说代价最低的那种方案去真正的执行查询。那么成本是怎么计算的呢&#xff0c;其实在 MySQL 中一条查询语句的执行成本是由下边这两个方面组成的: I/O 成本 …

【Linux】Linux基础IO(下)

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;Linux &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【Linux】…

JVM 内存区域

JVM内存结构模型 程序计数器&#xff1a; 1.线程私有的&#xff0c;是一块较小的内存空间&#xff0c;当前线程所执行的字节码的行号指示器 2.每个线程都有一个独立的程序计数器&#xff0c;各线程之间程序计数器互不影响&#xff0c;独立存储 3.此内存区域是唯一一个在java虚拟…

u系 kdump查看配置

V4 桌面&#xff1a; 如果能上外网配置网络源安装软件包&#xff1a; 会自动安装以下几个包&#xff08;不能连接外网直接安装一下几个包即可&#xff09;&#xff1a; 查看kdump配置&#xff1a; Kdump-config show 可以看到USE_KDUMP1 &#xff0c;生成的vmcore文件在/var…

4种互斥机制比较

4种互斥机制 关中断禁止任务切换信号量互斥信号量 关中断 关中断&#xff08;Disable Interrupts&#xff09;&#xff1a;通过禁用中断来实现互斥。在关中断期间&#xff0c;任何中断请求都会被忽略&#xff0c;从而确保了临界区的独占性。然而&#xff0c;这种方法会导致系统…

【软考篇】中级软件设计师 第二部分(二)

中级软件设计师 第二部分&#xff08;二&#xff09; 十三. 死锁问题十四. 段页式存储14.1 页式存储14.1.1 缺页中断14.1.2 页面置换算法 14.2 段式存储14.3 段页式存储 十五. 索引文件十六. 文件目录16.1 树形目录结构16.2 位示图 十三. 死锁问题 多刷题 系统不可能发生死锁的…

⑨【MySQL事务】事务开启、提交、回滚,事务特性ACID,脏读、幻读、不可重复读。

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ MySQL事务 ⑨【事务】1. 事务概述2. 操作事务3…

面试经典(6/150)轮转数组

面试经典&#xff08;6/150&#xff09;轮转数组 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 以下为自己的思路&#xff0c;我不明白最终的返回值为什么有误&#xff0c;好像是题目里要求原地解决问题&#xff0c;而我创…

Live800:客服行业的发展历程及未来前景

随着信息技术和互联网的高速发展&#xff0c;客服行业也在不断变革和发展。客服行业是一个服务型的行业&#xff0c;其发展历程也与人们对服务需求的变化密切相关。本文将介绍客服行业的发展历程和未来前景。 客服行业的发展历程 20世纪70年代&#xff0c;客服行业主要以电话服…

【开源】基于Vue.js的校园二手交易系统的设计和实现

目录 一、摘要1.1 项目介绍1.2 项目详细录屏 二、功能模块2.1 数据中心模块2.2 二手商品档案管理模块2.3 商品预约管理模块2.4 商品预定管理模块2.5 商品留言板管理模块2.6 商品资讯管理模块 三、实体类设计3.1 用户表3.2 二手商品表3.3 商品预约表3.4 商品预定表3.5 留言表3.6…

如何安装WampServer并结合内网穿透工具实现公网访问内网服务

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. WampServer下载安装二. WampServer启动三. 安装cpolar内网穿透3.1 注册账号…

卡尔曼家族从零解剖-(06) 一维卡尔曼滤波编程(c++)实践、透彻理解公式结果

讲解关于slam一系列文章汇总链接:史上最全slam从零开始&#xff0c;针对于本栏目讲解的 卡尔曼家族从零解剖 链接 :卡尔曼家族从零解剖-(00)目录最新无死角讲解&#xff1a;https://blog.csdn.net/weixin_43013761/article/details/133846882 文末正下方中心提供了本人 联系…

linux高级篇基础理论(详细文档)二

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️不能因为人生的道路坎坷,就使自己的身躯变得弯曲;不能因为生活的历程漫长,就使求索的 脚步迟缓。 ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xff1a;云计算技…

NOIR脑机接口机器人——让脑机接口通过少样本学习实现做家务的能力

一、概述 大脑与机器人接口&#xff08;BRI&#xff09;是人类艺术、科学和工程的集大成之作&#xff0c;其影响已经贯穿于无数科幻作品和创意艺术之中&#xff0c;如《黑客帝国》和《西部世界》等。然而&#xff0c;要真正实现BRI并创造出能够与人类完美协同运作的机器人系统…