Databend 内幕大揭秘第一弹 - minibend 简介

news2024/9/20 10:29:20

minibend ,一个从零开始、使用 Rust 构建的查询引擎。这里是 minibend 系列技术主题分享的第一期,来自 @PsiACE 。

前排指路视频和 PPT 地址
视频(哔哩哔哩):https://www.bilibili.com/video/BV1Ne4y1x7Cn

PPT:https://psiace.github.io/databend-internals/minibend/ppt/minibend-001-basic-intro.pdf

minibend: what, why, how

minibend 是什么

minibend 是一款从零开始、使用 Rust 构建的查询引擎。

查询引擎是数据库系统的一个重要组件,需要具备以下几点能力:

  • 访问数据
  • 提供查询接口
  • 返回查询结果

通常我们会使用 SQL 也就是结构化查询语言进行交互。

minibend 同时也是 Databend Internals,或者说 Databend 内幕大揭秘 这个手册的实战部分。Databend 内幕大揭秘 将会透过 Databend 的设计与实现,为你揭开面向云架构的现代数据库的面纱。

为什么要设计 minibend

databend

特别是在团队已经孵化出 Databend 这个现代开源云数仓的前提下,为什么还需要这样一个项目?

先回到 Databend 内幕大揭秘 的初衷,设立这个项目是为了吸引更多人参与到 Databend 的学习、开发和生态建设中,所以目标受众定位在:

  • 高校计算机专业的学生。
  • 想从事数据库研发的开发者。
  • 对数据库整体运作原理感兴趣的朋友。

但是,Databend 的更新迭代速度、代码量都意味着对刚开始接触 Rust 并尝试参与研发的新朋友会面临一个比较高的门槛。

从现存的教程上看,或多或少存在一些问题:

  • 不是 Rust 实现(切换语言和生态需要一些努力)
  • 缺乏 step by step 的体验(细节上需要更深入和艰苦的挖掘)
  • 从设计思想到实现逻辑上可能与 Databend 有比较明显的差异(存算分离、面向云,这些可能都会有一些抉择)。

所以开启一个新的项目作为连接新开发者和 Databend 之间的纽带就成为一种自然的选择。

P.S. minibend 致力于解决这些问题,但可能很难完全解决,但至少,先开始运作起来。

minibend 这个项目计划怎样进行

首先,minibend 会提供视频、文章和代码三种材料。文章和代码将会同步到 Databend 内幕大揭秘 的 Repo 中,而视频则会发布到 Databend 的 B 站官方帐号下。欢迎大家持续关注。

Databend 内幕大揭秘:https://psiace.github.io/databend-internals/
Databend(哔哩哔哩):https://space.bilibili.com/275673537

更新频率大概是每个月一到两期。内容上会包含必要的相关知识导读、设计和实现相关的说明、并进行回顾和展望。当然,也会不定期精选一部分论文摘要供大家进一步研讨和学习。

数据库基础概念

在这个部分,我们不会深入数据库的细节,只是从部分组件的视角上进行观察。

存储

存储解决的是两个问题,存在哪 以及 怎么存

对于“怎么存”,不同背景的朋友可能会考虑到一些不同的细节,但大多数时候,可以想象到一个基本的模式是:数据以特定格式写入到某几类文件中,比如 Parquet 甚至 CSV 。

但是“存在哪”呢?

storage Level

过去的一些存储方案更加关注上图所示的存储体系结构,将需要在线处理的数据存放在闪存和硬盘中,用于备份的数据放入光盘和磁带。

云存储的兴起和网络带宽的不断提高带来了一些新的变化:云存储能够支持远程保存数据和文件,并通过网络连接进行访问。不仅可以节约拓展物理器件所带来的人力物力消耗,并能够提供更好的弹性以便于即时增减容量,还支持按需按量付费从而做到更好的成本管理与控制。

Databend 早期的实现是包含一套分布式文件系统的,但到现在,存储的重心完全转移到云厂商提供(AWS S3, Azure Blob 等)或者自托管(MinIO 等)的云存储之上。

尽管云存储越来越重要,但原有的经验和见解依然有效,我们仍然可以使用缓存和并行技术来改善性能,利用冗余来提高可靠性。

索引

引入索引的好处在于加快数据查询的速度,而缺点则在于构建和维护索引同样需要付出代价。

b tree disk

不同的索引可以针对不同的场景提供优化,B Tree 能够加速范围查询,而等值查询就可以使用 Hash 索引,BitMap(或者说更常用的 Bloom 索引)可以方便判断数据是否存在。

Databend 的索引无需人为创建,由部署的实例自行维护。同时也采用了像 Xor 索引 这样的新技术来进一步加速查询并提高空间利用率。

查询执行

尽管有各种各样的查询引擎,但具体到查询执行的环节大同小异,这里以 Databend 为例,简单讲一下过程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uz4mJ9E8-1673837073166)(https://psiace.github.io/databend-internals/the-basics/executor-in-query-process/01-query-steps.png)]

  1. 解析 SQL 语法,形成 AST(抽象语法树)。
  2. 通过 Binder 对其进行语义分析,并且生成一个初始的 Logical Plan(逻辑计划)。
  3. 得到初始的 Logical Plan 后,优化器会对其进行改写和优化,最终生成一个可执行的 Physical Plan 。
  4. 通过 Optimizer 生成 Physical Plan 后,将其翻译成可执行的 Pipeline 。
  5. Pipeline 则会交由 Processor 执行框架进行计算。

那么近年来新兴数据库大多受到 Morsel-Driven Parallelism 这篇论文的启发,在运行时确定任务的并行度,按流水线的方式执行操作,并通过调度策略来尽量保证数据的本地化,在实现 load banlance 的同时最小化跨域数据访问。

同时,引入列式存储和向量化执行的技术,可以避免不必要的缓存和 I/O 资源浪费,同时,节约处理数据时需要传递的数据量,为进一步优化提供更多空间。

查询优化

查询执行的路径并非一成不变,不同的执行计划在不同场景下性能也存在差异,如何为查询选择合适的计划就是查询优化需要关注的内容。

  • 更快的速度(更低的延迟)。
  • 在 OLTP 场景下,则更强调性价比。
  • 而对于 OLAP 场景,则追求更的高吞吐量。

下面的图片展现的是一种典型的查询优化,对 JOIN 进行重排。

JOIN Reorder

目前有两种主要的查询优化方案,一种是基于关系代数和算法的等价优化方案,一种是基于评估成本的优化方案。根据命名,不难看出优化的灵感来源和这两种方案在优化上的取舍。

那么如何进行查询优化呢?查询优化通常包含以下四个步骤:

  • 构建框架来列举可能的计划
  • 编写转换规则
  • 引入成本模型来评估不同的计划
  • 选择最理想的计划

Databend 引入了基于规则的 Cascades 优化器,通过自顶向下探索、模式匹配以及记忆化来提供更好的查询优化能力。

大规模并行处理

大规模并行处理是大数据计算引擎的一个重要特性,可以提供高吞吐、低时延的计算能力。那么,当我们在讨论大规模并行处理时,究竟在讨论什么?

mpp

大规模并行处理(MPP,Massively Parallel Processing)意味着可以由多个计算节点(处理器)协同处理程序的不同部分,而每个计算节点都可能具备独立的系统资源(磁盘、内存、操作系统)。

计算节点将工作拆分成易于管理、调度和执行的任务执行,通过添加额外的计算节点可以完成水平拓展。随着计算节点数目的增加,对数据的查询处理速度就越快,从而减少大数据集上处理复杂查询所需的时间。

在近些年,MPP 和分布式设计往往会同时出现在同一套系统中。

分布式

分布式

从某种视角上看,分布式系统与 MPP 系统有着惊人的相似。比如:通过网络连接、对外作为整体提供服务、计算节点拥有资源等。但是这两种架构仍然会有一些不同。

  • 从设计目标上看,分布式系统致力于改善系统的可靠性和可用性,而 MPP 系统需要充分利用计算节点的并行能力从而提高整体性能。
  • 具体到实现上,分布式系统降低了对网络的需求,采用局域网或广域网相连,拓展性进一步增强。而 MPP 系统为了充分利用计算节点的处理能力,依赖高速网络进行通讯。
  • 同时,由于节点地位不同,分布式系统除了协同执行任务之外,还具有自治执行任务的能力;而 MPP 系统则专注于任务的协同执行。

Rust 不完全指南

刚刚介绍了数据库相关的一些基本概念,现在让我们将目光转向 Rust ,来一同了解这个正在走向流行的编程语言。

The Rust Programming Language

rust

Rust 官方宣传语是:Rust 是一门赋予每个人构建可靠且高效软件能力的语言,现在距离它第一个版本发布也已经过去10年。

Rust 没有运行时和垃圾回收,速度快且内存利用率高,几乎可以与 C 和 C++ 竞争。

Rust 的类型系统和所有权模型为内存安全和线程安全提供保障,在编译期就能够消除各种各样的错误。

特别值得一提的是,Rust 工具链内置很多实用工具,可以切实改善生产力:包管理器、构建工具、格式化程序、用于代码审计的 Clippy 等等。

函数

#[allow(dead_code)]
// Functions
// `i32` is the type for 32-bit signed integers
fn add2(x: i32, y: i32) -> i32 {
    // Implicit return (no semicolon)
    x + y
}

上面函数是两个 32 位整数相加,返回值也是一个 32 位整数。值得注意的是,我们需要标注返回值类型,而函数体中的 x + y 是一种隐式返回,所以不需要添加 return 关键字,当然,也不需要在末尾添加分号。只添加末尾分号的话,则会将其视为普通语句执行,就没有返回值了(报错)。

// This is the main function
fn main() {
    // Statements here are executed when the compiled binary is called

    // Print text to the console
    println!("Hello World!");
}

经典的 Hello World 程序,大家应该会感觉到熟悉。main 函数也是 Rust 程序的入口点。通过调用 println! 这个宏,可以输出文本到终端。

类型

// Struct
struct Point {
    x: i32,
    y: i32,
}

// A struct with unnamed fields, called a ‘tuple struct’
struct Point2(i32, i32);

// Enum with fields
enum OptionalI32 {
    AnI32(i32),
    Nothing,
}

// Generics //
struct Foo<T> { bar: T }

// Traits (known as interfaces or typeclasses in other languages) //
trait Frobnicate<T> {
    fn frobnicate(self) -> Option<T>;
}

impl<T> Frobnicate<T> for Foo<T> {
    fn frobnicate(self) -> Option<T> {
        Some(self.bar)
    }
}

除了基本的字符串、整数、浮点数、布尔类型之外,Rust 还支持结构体和枚举类型,代码片段提供了一个基本的例子。为这些类型可以实现特定的方法,以支持各种各样的操作,通用的接口可以使用 trait 关键字进行定义。

模式匹配

let foo = OptionalI32::AnI32(1);
match foo {
    OptionalI32::AnI32(n) => println!("it’s an i32: {}", n),
    OptionalI32::Nothing  => println!("it’s nothing!"),
}

// Advanced pattern matching
struct FooBar { x: i32, y: OptionalI32 }
let bar = FooBar { x: 15, y: OptionalI32::AnI32(32) };

match bar {
    FooBar { x: 0, y: OptionalI32::AnI32(0) } =>
        println!("The numbers are zero!"),
    FooBar { x: n, y: OptionalI32::AnI32(m) } if n == m =>
        println!("The numbers are the same"),
    FooBar { x: n, y: OptionalI32::AnI32(m) } =>
        println!("Different numbers: {} {}", n, m),
    FooBar { x: _, y: OptionalI32::Nothing } =>
        println!("The second number is Nothing!"),
}

模式是 Rust 中特殊的语法,它用来匹配类型中的结构,看起来有点像 switch,但要更加强大和简洁。无论类型是简单还是复杂,结合使用模式和 match 表达式以及其他结构可以提供更多对程序控制流的支配权。通过将一些值与模式相比较来使用它。如果模式匹配这些值,就可以对值的部分进行相应处理。

控制流

// for and ranges
for i in 0u32..10 {
    print!("{} ", i);
}
println!("");
// prints `0 1 2 3 4 5 6 7 8 9 `

// `if` as expression
let value = if true {
    "good"
} else {
    "bad"
};

// `while` loop
while 1 == 1 {
    println!("The universe is operating normally.");
    // break statement gets out of the while loop.
    //  It avoids useless iterations.
    break
}

// Infinite loop
loop {
    println!("Hello!");
    // break statement gets out of the loop
    break
}

上面是一些常见的控制流语法,for 循环和范围迭代看起来和其他语言很相似;而通过 let - if 语句,可以轻松将 if 当作表达式来使用;当然,Rust 同样支持 while 循环和无限 loop 循环。

内存安全与指针

// Owned pointer – only one thing can ‘own’ this pointer at a time
// This means that when the `Box` leaves its scope, it can be automatically deallocated safely.
let mut mine: Box<i32> = Box::new(3);
*mine = 5; // dereference
// Here, `now_its_mine` takes ownership of `mine`. In other words, `mine` is moved.
let mut now_its_mine = mine;
*now_its_mine += 2;

println!("{}", now_its_mine); // 7
// println!("{}", mine); // this would not compile because `now_its_mine` now owns the pointer

Owned Pointer,一次只能有一个对象“拥有”此指针,这意味着当 Box 离开其作用域时,它可以安全地自动释放。

// Reference – an immutable pointer that refers to other data
// When a reference is taken to a value, we say that the value has been ‘borrowed’.
// While a value is borrowed immutably, it cannot be mutated or moved.
// A borrow is active until the last use of the borrowing variable.
let mut var = 4;
var = 3;
let ref_var: &i32 = &var;

println!("{}", var); // Unlike `mine`, `var` can still be used
println!("{}", *ref_var);
// var = 5; // this would not compile because `var` is borrowed
// *ref_var = 6; // this would not either, because `ref_var` is an immutable reference
ref_var; // no-op, but counts as a use and keeps the borrow active
var = 2; // ref_var is no longer used after the line above, so the borrow has ended

Reference – 引用其他数据的不可变指针。当引用某个值时,我们称该值已被 “借用” 。当一个值被不可变借用时,它不能被修改或移动。借用直到在最后一次使用借用变量之前会一直处于活跃状态。

// Mutable reference
// While a value is mutably borrowed, it cannot be accessed at all.
let mut var2 = 4;
let ref_var2: &mut i32 = &mut var2;
*ref_var2 += 2;         // '*' is used to point to the mutably borrowed var2

println!("{}", *ref_var2); // 6 , // var2 would not compile.
// ref_var2 is of type &mut i32, so stores a reference to an i32, not the value.
// var2 = 2; // this would not compile because `var2` is borrowed.
ref_var2; // no-op, but counts as a use and keeps the borrow active until here

可变引用,如果你有一个对该变量的可变引用,你就不能再创建对该变量的引用。

上面的这些 Rust 片段节选自 Learn X in Y minutes ,只进行了一些粗浅的介绍。

如果想要进一步学习,建议查阅以下资料:

  • The Rust Programming Language
  • Databend 早期关于 Rust 的系列视频。
    • Rust 新手入门系列课程
    • Rust 培养提高计划

前进四:回顾与展望

回顾

首先我们介绍了 minibend 这个系列课程,一方面,这会是一个从零开始、使用 Rust 构建的查询引擎;另一方面,它会参考 Databend 的设计,并致力于降低数据库内核开发的门槛。

而在数据库相关基础知识的部分,云存储为现代数据库设计带来了一些新变化,而不同的索引又可以为不同的查询场景带来性能优化,接着是查询执行和查询优化的相关知识,以及对大规模并行处理和分布式技术的介绍。

Rust 不完全指南里,从函数、类型、模式匹配、控制流、内存安全与指针进行了一个简单的介绍,为阅读 Rust 代码提供了一个简单的基础。

展望

下一期,我们将会介绍 Apache Arrow - 一种列式存储的内存格式规范,以及查询引擎中的类型系统,然后试着写一些关于数据源的代码。

阅读材料

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kS6TBWLI-1673837073168)(https://psiace.github.io/databend-internals/minibend/001-basic-intro/minibend-001-basic-intro_22.png)]

本期课程推荐两本书给大家:

一本是 The Rust Programming Language ,这是 rust 官方出品的 Rust 书籍,一般被称作 the book 。

另一本是 How Query Engines Work ,Andy 同时也是 Datafusion 和 Ballista 的作者,不过这本书使用的是 kotlin 。

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

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

相关文章

Unity 之 Addressable可寻址系统 -- 资源加载和释放

可寻址系统资源 -- 加载和资源释放 -- 进阶&#xff08;二&#xff09;一&#xff0c;资源加载1.1 同步异步对比1.2 三种加载模式二&#xff0c;释放资源2.1 基础概念2.2 实例演示2.2.1 示例演示一2.2.2 示例演示二2.3 注意事项概述&#xff1a;本篇文章从资源加载的方式和具体…

Spring官方提供【CSRF攻击】解决方案

步入正文 Cookie cookie是我们常见用来保存用户态信息&#xff0c;cookie跟随我们的请求自动携带。在同一域名下的请求&#xff0c;cookie总是自动携带。 用户态: 当前登入者的用户信息 以上的特性会导致一个潜在漏洞-CSRF CSRF CSRF一般指跨站请求伪造。 跨站请求伪造&…

长安链合约标准协议启动建设,邀请社区用户评审

智能合约是区块链摆脱第三方&#xff0c;实现验证、执行业务逻辑的“看不见的手”。随着联盟链产业落地进入快车道&#xff0c;需要面对的应用场景更加多样&#xff0c;智能合约标准协议作为推动联盟链应用生态繁荣的重要一环也需要加速推进发展。 区块链技术正在发展中规范。…

PHP 连接 MySQL

PHP 5 及以上版本建议使用以下方式连接 MySQL : MySQLi extension ("i" 意为 improved)PDO (PHP Data Objects) 在 PHP 早期版本中我们使用 MySQL 扩展。但该扩展在 2012 年开始不建议使用。 我是该用 MySQLi &#xff0c;还是 PDO? 如果你需要一个简短的回答&…

C语言 栈的应用 计算简单的中缀表达式

代码简介&#xff1a;下面的代码实现了计算简单的中缀表达式&#xff1a;只可以处理一位正整数的四则运算及括号。是栈的简单应用&#xff0c;要实现中缀表达式运算需要用两个栈&#xff0c;一个存储数字的栈和一个存储运算符的栈&#xff0c;因为懒得写两遍不同的栈上的操作&a…

增益自适应PI控制器+死区过滤器(Smart PLC向导PID编程应用)

增益自适应和死区过滤器如果不和S7-200 SMART PLC PID向导组合实现,大家可以自行编写优化的PID指令。算法起始非常简单,具体实现过程大家可以参看下面的文章链接, 三菱增量式PID+死区过滤器 三菱PLC增量式PID算法FB(带死区设置和外部复位控制)_RXXW_Dor的博客-CSDN博客关于…

【论文简述】High-Resolution Optical Flow from 1D Attention and Correlation(ICCV 2021)

一、论文简述 1. 第一作者&#xff1a;Haofei Xu 2. 发表年份&#xff1a;2021 3. 发表期刊&#xff1a;ICCV 4. 关键词&#xff1a;光流、代价体、自注意力、高分辨率、GRU 5. 探索动机&#xff1a;小分辨率对于网络性能有影响&#xff0c;并且现实场景中大多为高分辨率的…

Minecraft 1.19.2 Fabric模组开发 04.动画效果方块

我们本次尝试在1.19 Fabric中制作一个具有动画效果的方块 效果演示效果演示效果演示 首先&#xff0c;请确保你的开发包中引入了geckolib依赖&#xff0c;相关教程请参考:Minecraft 1.19.2 Fabric模组开发 03.动画生物实体 1.首先我们要使用geckolib制作一个物品和对应的动画…

eCharts工具类

ECharts是一款基于JavaScript的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。ECharts最初由百度团队开源&#xff0c;并于2018年初捐赠给Apache基金会&#xff0c;成为ASF孵化级项目。 ECharts官方地址…

【数据结构与算法】顺序表的原理及实现

1.什么是顺序表 顺序表是用一段物理地址连续的存储单元进行存储元素的线性结构&#xff0c;通常是以数组进行存储。通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。 2.顺序表的实现 判断顺序表是否为空表public boolean isEmpty()判断顺序表是否满publi…

excel功能小技巧:自动求和的注意事项

在EXCEL里有个非常方便的按钮&#xff0c;叫做自动求和。不需要输入公式&#xff0c;直接一点&#xff0c;即可得出求和结果。由于它操作上的便利&#xff0c;所以深受小白喜爱。不过看着简单的自动求和按钮&#xff0c;实际上却藏着不少暗坑&#xff0c;稍不留神&#xff0c;可…

juc系列(2)--线程的使用及原理

目录线程创建线程ThreadRunnableCallable线程方法APIrun startsleep yieldjoininterrupt打断线程打断 park终止模式daemon不推荐线程原理运行机制线程调度未来优化线程状态查看线程线程 创建线程 Thread Thread 创建线程方式&#xff1a;创建线程类&#xff0c;匿名内部类方…

R-P-Faster R-CNN day65 读论文:高分辨率遥感影像综合地理空间目标检测框架

An Efficient and Robust Integrated Geospatial Object Detection Framework for High Spatial Resolution Remote Sensing Imagery 1. Introduction3. Overview of the Proposed R-P-Faster R-CNN Framework3.1. 有效集成区域建议网络与目标检测Faster R-CNN框架3.1.2. RPN与…

java反射在spring ioc和aop中的应用

java反射在spring ioc和aop中的应用 反射&#xff1a; 1.反射是什么&#xff1f; 程序运行时&#xff0c;通过类名能够获得类的属性和方法。使用方式如下 Class clazz Class.ForName(“Student”)Class clazz Student.class;Class clazz student.getClass(); 获取到claz…

java JUC 中 Object里wait()、notify() 实现原理及实战讲解

1.Object中的wait()实现原理 在进行wait()之前&#xff0c;就代表着需要争夺Synchorized&#xff0c;而Synchronized代码块通过javap生成的字节码中包含monitorenter和monitorexit两个指令。 当在进加锁的时候会执行monitorenter指令&#xff0c;执行该指令可以获取对象的mon…

前端与HTML

本节课程围绕“前端要解决的基本问题”及“什么是 HTML ”两个基本问题展开&#xff0c;了解 HTML 高效的编写原则。 什么是前端 使用web技术栈解决多端的人机交互问题 技术栈 html&#xff08;内容&#xff09; css &#xff08;样式&#xff09;javascript &#xff08;行…

linux部署KubeSphere和k8s集群

上一篇文章讲述了在单个节点上安装 KubeSphere和k8s&#xff0c;这节主要讲解k8s多节点集群部署 准备环境&#xff1a;Alibaba Cloud Linux系统3台机器第一步&#xff1a;设置主机名称hostname--(3台机器都设置) hostnamectl set-hostname master hostnamectl set-hostname nod…

智云通CRM:为什么你总是在请客,但业绩却上不来?

王总是一位企业老板&#xff0c;社会资源比较好&#xff0c;在过去的一年里&#xff0c;他新代理的一个保健品的项目&#xff0c;需要销售产品和招募合伙人。他想利用自己的人脉资源做销售&#xff0c;但他的销售过程并不顺利&#xff0c;在连续主动邀约之后效果不佳。 于是他…

2023/1/15 JS-变量提升与函数提升 执行上下文

1 变量提升与函数提升 变量声明提升 通过 var 声明的变量&#xff0c;在声明语句之前就可以访问到 - 值: undefined <script>console.log(a); // undefinedvar a 10 </script>函数声明提升 通过 function 声明的函数, 在声明语句之前就可以直接调用 - 值: 函数…

走近软件生态系统

生态系统&#xff08;Ecosystem&#xff09;原本是一个生物学术语&#xff0c;意思是由一些生命体相互依存、相互制约而形成的大系统&#xff0c;就像我们学生时代在生物学课堂上学到的那样。隐喻无处不在&#xff0c;人们把这个术语移植到了 IT 领域中来&#xff0c;比如我们常…