百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

深入解析分布式数据库 ZNBase 的 SQL 引擎优化

mhr18 2024-10-12 04:35 17 浏览 0 评论

往期回顾: 深入解析ZNBase分布式SQL引擎架构的五大服务组件

导读

前文提到,ZNBase 是由浪潮开源的一款 NewSQL 分布式数据库,基于谷歌 Spanner+F1 的论文设计,完美继承了 Spanner 的设计理念,实现了基于对等架构的分布式 SQL 引擎。ZNBase 的 SQL 引擎包含连接、编译、缓存、分布式日志和分布式执行五大服务组件,实现了多集群多节点协同的高效计算,大大提升了用户的查询效率。

为了进一步提升 SQL 引擎的性能,ZNBase 研发团队结合实际业务需求,在原有架构的基础上,针对 SQL 引擎的编译服务、执行服务、算法等方面进行了一系列深度定制化的优化改进工作。本文将这些改进工作逐一展开介绍。


ZNBase 针对 SQL 引擎的优化改进


1.编译服务优化


1.1 类型、功能、语法兼容

随着日益增多的场景需要,ZNBase 陆续完善了对 PostgreSQL 、Oracle、MySQL 语法、类型、函数的兼容。

1.2 计划优化

1.2.1 直方图

ZNBase 还扩展了统计信息功能,除了:表的行数,表中列的 Distinct 值(某一列的唯一值总共有多少条),还额外引入了直方图。为 CBO 的优化提供了更多的一句。

统计信息获取的简单流程如下:

对每个 range 进行抽样,用蓄水池算法生成样本集合,然后用样本进行各种统计信息的预估,将结果通过写入函数 writeResults,写进系统表 system.table_statistics 中。

1.2.2 执行计划管理

ZNBase 扩展了对执行计划的管理,包括执行计划绑定、自动捕获绑定、自动更新绑定等。执行计划绑定功能使得可以在不修改 SQL 语句的情况下选择指定的执行计划。用户通过绑定执行计划,可以将计划存入 ZNBase 中,下次再执行解析后计划相同的 SQL 语句时,只要取出之前存入的计划即可,省去了构建计划的时间。ZNBase 还会智能地自动捕获执行频率较多的并且用户之前没有手动为其创建绑定的 SQL 语句,在后台自动为其创建计划绑定。

由于表数据的变化,如:数据变化、数据结构变化、统计信息变化,可能会导致之前绑定的执行计划执行效率降低,ZNBase 将自动检测执行时间,将绑定好的执行计划进行优化,为用户提高复合当前数据场景的更高的执行效率。


2. 执行优化


2.1 矢量算子

ZNBase 还引入了矢量算子,相比基于 Goetz Graefe 论文的“火山”模型,“矢量”模型在计算行数明显大于列数的场景下,性能会有极大的提升。

从原理上讲,这是用一系列专门针对数据类型和计算的特定编译循环代替了通用的类似于解释器的 SQL 表达式评估器,因此计算机可以连续执行许多更简单的任务,大大节省了重复的计算所需要的时间。配合 ZNBase 团队开发的列式存储,查询性能还将有进一步的提升。

目前矢量算子支持的类型有:Array、BIT、BOOL、BYTES、COLLATE、DATE、DECIMAL、INET、INT、INTERVAL、JSONB、SERIAL、TIME、TIMESTAMP、TIMESTAMPTZ、UUID、FLOAT、STRING 等。

目前 ZNBase 支持的矢量算子有:Noop、TableReader、Distinct、Ordinality、Hashjoiner、MergeJoiner、Sorter、Windower 等。

举例来说,请考虑一个包含三列的 People 表:Id,Name 和 Age。在火山模型中,每个数据行由每个算子处理一次 —— 一种逐行执行方法。相比之下,在矢量化执行引擎中,我们一次传递了有限大小的面向列数据的批处理。我们使用一组列,而不是使用元组数组的数据结构,其中每一列都是特定数据类型的数组。在该示例中,分批处理将由一个Id的整数数组,一个 Name 的字节数组和 Age 的整数数组组成。下图显示了两个模型中数据布局之间的区别:

火山模型


矢量模型

SELECT Name, (Age - 30) * 50 AS Bonus FROM People WHERE Age > 30;

这样的语句查询,在火山模型中,顶级用户向 Project 算子请求一行,该请求被传播到底层的 Scan 算子。扫描从键值存储中读取一行,并将其传递给 Select,Select 将检查该行是否通过了 Age> 30 的谓词。如果该行通过了检查,则将其返回给 Project 算子以计算 Bonus = (Age - 30) * 50 作为最终输出。


火山模型流程图

一次处理一行,对于每一行,我们都在调用一个完全通用的标量表达式的过滤器!表达式可以是任何东西:乘法,除法,相等检查或内置函数,甚至可以是一长串这样的东西。由于这种通用性,计算机在每一行上都有很多工作要做——必须在甚至无法执行任何工作之前检查表达式的含义。与编译后的语言相比,这种计算方式与解释型语言同样麻烦。

在矢量化模型中,我们采用不同的方式。每个矢量化算子背后的原理是在执行期间不允许任何自由度或运行时选择。这意味着对于任务,数据类型和属性的任意组合,应该由一个专门的算子来负责这项工作。对于示例查询,用户从算子链中请求一批。每个算子都向其子级请求一批,执行其特定任务,然后将一批返回给其父级。

矢量模型流程图

为了对此进行可视化,请考虑由 SelectIntGreaterThanInt 处理的 People batch。该算子将选择所有大于 30 的 Age 值。这个新的 sel_age batch 然后传递到 ProjectSubIntInt 算子,该算子执行简单的减法运算以生成 tmp batch。最后,将这个 tmp batch 传递给 ProjectMultIntInt 算子,该算子将计算最终 Bonus =(Age-30)* 50 值。

矢量模型流程图


2.2 并行优化

在ZNBase的开发过程中也对算子进行了优化,提高了运算效率。

2.2.1 tablereader 并行

Tablereader 通过拆分 Span 进行并行的 baRequest 下发读取数据,返回的数据封装进 baResponse 里面,放入管道由 tablereader 进行并行处理。

tablereader的并行分为以下几步:

Step1:Span 拆分,逻辑计划完成后会生成一个 Span ALL(索引、主键查询除外),Span ALL 会根据 table 的 range 边界拆分从成多个范围更小的 Span,每个 Span 会获得相应的 range 信息,根据 rangeID 可以取得对应 range 的副本信息,再根据副本选择策略(就近选择、随机选择、lease holder(默认)),获取到对应副本的 nodeID,再将该 Span 放入一个 Map 结构(Map[nodeID][]Span)中;

当 tablereader 下发到了对应节点后,再将 Spans 进行均匀分配进 tablereader 的各个 worker fethcer 当中进行并行的数据读取:

Step2:BatchRequest 下发,对应节点的 tablereader 的每个 fetcher 的 spans 的每一个 span 会封装为一个 ScanRequest 请求,多个 ScanRequest 请求封装进一个 BatchRequest(BacthRequest 请求中 header 信息可以指定一次请求返回的最大 kv 数目),该 BacthRequest 经过分发层逻辑后下发至对应节点的对应 Store 进行数据查询,返回的数据封装为 BatchResponse,包含多个对应的 ScanResponse,将 ScanResponse 的 kv 数据放入 channel 中,再由每个 fetcher 绑定的 worker 进行 kv 解码以及后续的处理:

Step3:数据返回,每个 fetcher 的 worker 协程处理(经过 filter 或 render )完每行 kv 数据后都会放入一个 buffer 当中(默认 buffer 缓存<= 64 行),每个 worker 每完成一个 buffer 会将该 buffer 放入 tablereader 的结果管道中,提供 NextRow 和 NextChunk 两类接口供上层算子调用:


2.2.2 hashjoin 优化

原有 hashjoin 流程图如下:

原有执行流程存在如下问题

  • 单点流程是串行化执行,导致取出 outer 表的一行数据需要等待正在进行的 hashjoin 计算完成。
  • hashjoin 计算只由一个协程执行,数据量大的时候比较耗时。

经过优化后 hashjoin 由 3 个部分完成:

  1. Main thread:构造 hash 表;启动 Outer Fecther 和 Join Workers;从 join Woker 拿取计算结果,返回至上层;等待所有的 join worker 结束,更新状态为计算完成。
  2. Outer Fetcher(协程):循环读取 Outer 表每一行数据,将读取的数据通过 channel 传递给 Join Woker 进行计算;通知 Join Wokers Outer 表读取完成。
  3. Join Workers(协程):将 Outer Fetcher 发送来的数据进行 hash join 计算;将计算结果通过 channel 发送至 Main thread。

优化前后对比分析:

  • 设构造 inner(storedSide)一侧的 hash 表时间为 t1
  • 设读取一条 outer(otherSide)数据时间为 t2
  • 设执行一轮 hashjoin 时间为 t3
  • 设 outer(otherSide)表有 m 条数据

执行完 hashjoin:

优化前耗时≈t1+m*t2+m*t3

优化后耗时≈t1+(m/n)*t3

Δt≈(m(n-1))/n t3+m*t2

预期:随着 outer 表数据增多和 join worker 协程数增加,理论上优化越明显。

经优化后有如下优势:

  1. 计算读取分离:将读取 outer 表和 hash join 计算分离,使得读取 outer 表下一行数据不必再等待上一个 hash join 计算完成。
  2. 并行计算:启用多个 join worker 参与 hash join 计算,提高了并行度。


展望未来

大数据行业的发展促进了国产分布式数据库的演进,为适应这种发展大潮,云溪 NewSQL 数据库 ZNBase 也会促使分布式 SQL 引擎更趋向完善,未来会在语法上完全兼容 Oracle 等传统数据库,计划上加入更丰富的 HBO,完善 Cascade 框架,更加智能的提高用户的使用体验,执行上会兼容 HTAP 计算方式,顺应当下飞速增长的数据量,适配更多的大数据、人工智能、机器学习场景,提供一个更智能、更高效的 SQL 计算引擎。

参考文档 [1]. Volcano-An Extensible and Parallel Query Evaluation System

相关推荐

【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...

Pure Storage推出统一数据管理云平台及新闪存阵列

PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...

对Java学习的10条建议(对java课程的建议)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!

官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...

JDK21有没有什么稳定、简单又强势的特性?

佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...

「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了

在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...

Java面试题及答案最全总结(2025版)

大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...

数据库日常运维工作内容(数据库日常运维 工作内容)

#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...

分布式之系统底层原理(上)(底层分布式技术)

作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...

oracle 死锁了怎么办?kill 进程 直接上干货

1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...

SpringBoot 各种分页查询方式详解(全网最全)

一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...

《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略

《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...

LoadRunner(loadrunner录制不到脚本)

一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...

Redis数据类型介绍(redis 数据类型)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

RMAN备份监控及优化总结(rman备份原理)

今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...

取消回复欢迎 发表评论: