PostgreSQL技术内幕(十三)探究MPP数据库分布式查询分发Dispatcher

news2025/1/17 21:41:20

Dispatcher(分布式查询分发器)是MPP数据库的核心组件,所有的查询任务都要经过其进行分发,起着沟通用户到协调者(Coordinator,即QD)和执行调度的关键作用。

在这次的直播中,我们为大家介绍了Dispatcher基本原理和实现机制,并结合实际用例进行了操作演示。以下内容根据直播文字整理而成。

Slice与Gang的基本概念与分类

传统MPP数据库采用无共享Shared-Nothing架构来存储数据,节点之间不共享存储和计算资源,需要使用其他节点的数据时通常利用网络重分发。

图1:Greenplum数据库查询示意图

(图片来自 Greenplum: A Hybrid Database for Transactional and Analytical Workloads,SIGMOD '21 ,序号和箭头系本文作者所加)

以Greenplum为例,如图1所示,当用户连接到Coordinator(协调者节点)进行查询操作时,会通过Dispatcher组件将查询任务分配到不同的Segment,各Segment之间通过Interconnect模块来传输数据。当各节点查询执行完成后,由QD节点对查询结果进行收集和整理,再回传给用户。

需要注意的是,在查询任务执行时,用户不会和QE产生任何的连接,所有消息都是通过QD 来中转传递,这也是MPP 数据库的重要特征。整个过程中,涉及到两个重要的概念:

Slice:为了在查询执行期间实现最大的并行度,Greenplum会将查询计划工作划分为Slices。Slice是查询计划中可以独立进行处理的部分。查询计划会为Motion生成Slice,Motion的每一侧都有一个Slice。正是由于Motion算子将查询计划分割为一个个Slice,上一层Slice对应的进程会读取下一层各个Slice进程广播或重分布操作生成的数据,然后进行计算。

Gang:属于同一个Slice但是运行在不同的Segment上的进程,称之为Gang。在Greenplum中,共有Unallocated、Reader Gang、Writer Gang、Entry Reader、Singleton Reader五种类型的Gang。其中:

Unallocated运行在QD,一般只在Gather Motion将各个QE回传的结果收取并集时才会用到。

Reader Gang和 Writer Gang会经常用到,而且相关的查询计划会很复杂。

● 只读的查询仅包含 Reader Gang,包含写操作的查询才会使用Writer Gang。

● 一些既读又写的查询(例如 Create table as、Update Returning等)可能同时包含这两类 Gang。

● 这两类 Gang 都只有 1-Gang 和 N-Gang 的情况(其中N为Segment数量),优化器不会进一步划分出更细致的构造。

Entry Reader和Singleton Reader不太常见,通常在处理子查询或者其它需要保证系统中只有一份值的情形下出现。

MPP数据库分布式的特点,使其存储容量和计算能力突破了单机的上限,而成为多个Segment的总和。MPP 数据库的存储和计算紧耦合,对某一部分数据的计算应尽量在这一部分数据的存储节点上直接完成,这也使得所有表在创建时都有指定的分布模式,对该表的存储和计算都应按照该模式在集群的参与者节点上分布开来,从而形成“分布式”。

图2:Greenplum查询执行调度示意图

(来源同图1)

许多MPP数据库的初学者,经常会遇到一个问题,Slice和Gang是什么关系?

从二者的作用来看,Slice是对Plan的自然切割,Gang是对Slice的自然实现,两者之间是相互对应的。事实上,在内核代码中两者经常会交替使用。如果是优化器,会更多使用Slice;而在执行器、Dispatcher部分的代码就更多使用Gang。我们可以认为,Slice和Gang是完全对应的,前者侧重规划,后者侧重执行。

需要特别强调的是,Writer Gang ≠ Writer QE事实上,QE并不关心自己是不是Writer Gang,它的行为只与QD连接时下发的 isWriter 参数相关,这个参数通过libpq协议握手时 Startup 消息中的 gpqeid 参数下发到QE上,QE会据此设置Gp_is_writer 全局变量,从而控制QE的行为。

在Greenplum中,任何查询都只有一个Writer QE,而只有写查询才会有Writer Gang。因此,我们时常听到的“任何查询都有一个 Writer Gang”的论断是不严谨的。

查询分发

查询分发通常分为以下四个阶段:

1.创建Gang并初始化链接;

2.分发查询计划并执行;

3.回收Gang;

4.会话断开时,关闭链接并销毁Gang。

在所有查询中,Plan类查询最为复杂,它涵盖了其余三类查询的共用方法。接下来,我们以Plan类查询为例,展开介绍查询分发的实现流程。

💡Plan是指Select、Insert 这种可由 explain 指令查看执行计划的查询。这里有一个特例,CTAS(create table as)不属于 Plan,而是属于Utility 类别。

Plan类查询分发的入口点是cdbdisp_dispatchX函数,它会调用 AssignGangs 方法,通过两次先序遍历查询计划树,创建所有的Writer Gang,然后再创建其余Gang:

bool AssignWriterGangFirst(ExecSlice *slice, ...) [  if (slice->gangType == GANGTYPE_PRIMARY _WRITER) {    slice->primaryGang = AllocateGang(...);    return true;  } else {    ListCell *cell;    foreach(cell, slice-›children) {      if (AssignWriterGangFirst(cell->element, ...))        return true;    }  }  return false;}

AssignGangs 第一次遍历代码示例

bool InventorySliceTree(ExecSlice *slice, ...) {  if (slice->gangType == GANGTYPE_UNALLOCATED) {    slice->primaryGang = NULL;  } else if (!slice->primaryGang) {    slice->primaryGang = AllocateGang(...);  }  ListCell *cell;  foreach(cell, slice->children) {    InventorySliceTree(cell->element, ...);  }}

AssignGangs 第二次遍历代码示例

注意: AssignWriterGangFirst有一个“提前终止”的特性,表明查询计划只能有一个Writer Gang。

我们看到这两段代码都是调用 AllocateGang 函数完成具体一个Gang的初始化,上方的代码只能调用一次,下方的代码可以调用多次。

创建并初始化Gang

这里引入了另一个概念:Segment Type, 因为AllocateGang函数并不直接基于Gang Type去工作,需要将 Gang Type 转化为 Segment Type。二者之间对应关系如下:

● Writer Gang一定要求一个Writer QE,即一个 isWriter 为 true 的Gang;

● extended query(libpq 规定的扩展查询协议,即游标 CURSOR)一定要求一个Reader QE,即isWriter 为false 的 Gang;

● 其他情况(包括 Reader Gang 在内),适用任何节点。

通过以上的对应关系能够提供更好的兼容性,并进一步提高Gang进程的复用率。

下一步会调用:

cdbgang_createGang_async

cdbcomponent_allocateIdleQE

这两个函数。从名字可以看出, Gang的创建是一个异步的过程,会一次创建一个Gang中的所有链接,然后通过轮询等待创建完成。在创建过程中,首先判断 freelist 中回收上来的QE的相容性,如果相容则直接使用;否则异步分配新的Gang。

异步的过程如下所示:

// cdbgang_createGang_asyncint size = list_length(segments);for (i = 0; i < size; i++) {  segdbDesc = newGangDefinition->db_descriptors[i];  cdbconn_doConnectStart(segdbDesc, ...); // -> PQconnectStartParams -> PQconnectPoll  connStatusDone[i] = false;  pollingStatus[i] = PGRES_POLLING_WRITING;}for (;;) {  for (i = 0; i < size; i++) {    segdbDesc = newGangDefinition->db_descriptors[i];    if (connStatusDone[i]) continue;    switch (pollingStatus[i]) {      case PGRES_POLLING_OK:        ... // -> cdbconn_doConnectComplete        connStatusDone[i] = true;        continue;      case PGRES_POLLING_READING:         ... // set poll to wait till ready to read        break;      case PGRES_POLLING_WRITING:        ... // set poll to wait till write won't get blocked        break;      case PGRES_POLLING_FAILED: ... // throw exception    }  }    nready = poll(fds, nfds, poll_timeout);  if (nready > 0) {    for (i = 0; i < size; i++)      segdbDesc = newGangDefinition->db_descriptors[i];      pollingStatus[i] = PQconnectPoll(segdbDesc->conn);  }}

分发查询计划并执行

在这一阶段,以Gang 为单位异步分发M类型消息(QD到QE的消息为M类型消息,调用 exec_mpp_query 入口方法,而用户收到QD的消息则为 Q 类型消息,调用 exec_simple_query 入口方法)到所有Gang,并再次通过轮询等待分发完成。

回收Gang

回收Gang时,对每个Gang中的所有QE调用 cdbcomponent_recycleIdleQE 方法,将回收的QE放到freelist中。在下次分配时,会尽量使用已经创建好的链接。这种方法保证了Writer QE永远在freelist的前部,同时因为Writer QE具有良好的相容性,就使得freelist具备“有序”的性质,在分配 Gang(allocateIdleQE)时就无需遍历整个freelist ,提升了Gang的分配效率。

销毁Gang

销毁Gang时,通过libpq对每个idle QE进行终止握手,并释放状态。

其它类型查询

● Plan:到所有QE;parsed statement

● Command:到Writer QE;raw SQL

● Utility Statement:到Writer QE;parsed statement

● Set Command:到所有QE;raw SQL

掌握了Plan类型查询分发机制之后,就能更好地理解其余的三类查询,它们是 {到所有 QE,到Writer QE} 和对 {parsed statement, raw SQL} 两两组合形成的四种结果。这里的parsed statement指经过查询解析和规划后的PlannedStmt。

SharedSnapshot

Postgres有本地快照机制,保证本地进程的一致性,而Greenplum作为一个分布式系统,必须通过分布式快照,来确保跨Segment的全局一致性。

本地快照保证进程本地一致性,分布式快照保证跨Segment一致性,但在MPP数据库中,一个Segment可能有多个进程,这就需要一个新的机制来保证跨进程的Segment本地一致性。

SharedSnapshot作为基于共享内存的快照同步,成为沟通本地快照和分布式快照的一个桥梁。Postgres中的 SnapshotData自带了进程本地一致性,在这基础之上通过SharedSnapshot实现跨进程的Segment本地一致性,最终通过分布式快照DistributedSnapshot ,实现跨Segment的系统全局一致性,从而向用户提供全局一致的视图。

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

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

相关文章

Opencv中的RNG-随机绘图

在OpenCV中&#xff0c;RNG是一个随机数生成器类&#xff0c;用于生成各种类型的随机数&#xff0c;包括均匀分布或高斯分布的整数和浮点数。RNG类的实例化时可以接受一个无符号整数作为种子值&#xff0c;这个种子值决定了随机数生成序列的起点&#xff0c;相同的种子值将产生…

分布式id实战

目录 常用方式 特征 潜在问题 信息安全 高性能 UUID 雪花算法 数据库生成 美团Leaf方案 Leaf-segment 数据库方案 Leaf-snowflake 方案 常用方式 uuid雪花算法数据库主键 特征 全局唯一趋势递增信息安全 潜在问题 信息安全 如果id连续递增, 容易被爬虫, 批量下…

【动态规划】【字符串】2167移除所有载有违禁货物车厢所需的最少时间

作者推荐 【深度优先搜索】【树】【有向图】【推荐】685. 冗余连接 II 本文涉及知识点 动态规划汇总 LeetCode2167移除所有载有违禁货物车厢所需的最少时间 给你一个下标从 0 开始的二进制字符串 s &#xff0c;表示一个列车车厢序列。s[i] ‘0’ 表示第 i 节车厢 不 含违…

自动化上位机开发C#100例:如何用面向对象的方式封装雷赛运动控制卡EtherCAT总线卡(C#代码)

自动化上位机开发C#100例:雷赛运动控制卡EtherCAT总线卡C#封装类 文章目录 LTDMC.dll下载LTDMC.cs LTDMC.dll C#调用封装下载ICard.cs 运动控制卡接口Card.cs 运动控制卡抽象类CardLTDMC.cs 雷赛运动控制卡EtherCAT总线卡实现类CardList.cs 总线卡列表封装 LTDMC.dll下载 最新…

MLP-Mixer: AN all MLP Architecture for Vision

发表于NeurIPS 2021, 由Google Research, Brain Team发表。 Mixer Architecture Introduction 当前的深度视觉结构包含融合特征(mix features)的层:(i)在一个给定的空间位置融合。(ii)在不同的空间位置&#xff0c;或者一次融合所有。在CNN中&#xff0c;(ii) 是由N x N(N &g…

golangci-lint如何关闭typecheck

https://github.com/golangci/golangci-lint/issues/2912 typecheck是go源码的校验&#xff0c;无法通过.golangci.yml配置关闭 可以直接在golangci-lint源码层面关闭typecheck

Jmeter教程-JMeter 环境安装及配置

Jmeter教程 JMeter 环境安装及配置 在使用 JMeter 之前&#xff0c;需要配置相应的环境&#xff0c;包括安装 JDK 和获取 JMeter ZIP 包。 安装JDK 1.JDK下载 示例环境为Windows11环境&#xff0c;读者应根据实际环境下载JDK的安装包。 JDK下载地址&#xff1a; Java21 下载 …

windows自带录屏指南,让视频制作更简单!

随着网络技术的发展&#xff0c;录制电脑屏幕已成为了许多用户的常见需求&#xff0c;无论是录制游戏画面、软件教程还是制作演示视频&#xff0c;一款好用的录屏工具都至关重要。windows操作系统为用户提供了多种录屏工具&#xff0c;无需额外安装软件。本篇文章将介绍windows…

Guitar Pro 8.1 Mac 2024最新下载、安装、激活、换机图文教程

Guitar Pro 8是吉他手的终极工具箱,也是阅读和编辑乐谱的领先软件。26 年来,Guitar Pro 一直在帮助世界各地的音乐家学习弹吉他、创作歌曲以及转录和编辑歌集。 Guitar Pro是一款专业的吉他制谱软件&#xff0c;现在已更新至Guitar Pro8&#xff0c;新增了支持添加音频轨道、支…

19-树-填充每个节点的下一个右侧节点指针 II

这是树的第19篇算法&#xff0c;力扣链接。 给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NULL 。…

从入门到精通:AI绘画与修图实战指南

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 在这篇文章中&#xff0c;我们将深入探讨如何利…

JAVA高并发——人手一支笔:ThreadLocal

文章目录 1、ThreadLocal的简单使用2、ThreadLocal的实现原理3、对性能有何帮助4、线程私有的随机数发生器ThreadLocalRandom4.1、反射的高效替代方案4.2、随机数种子4.3、探针Probe的作用 除了控制资源的访问&#xff0c;我们还可以通过增加资源来保证所有对象的线程安全。比如…

2024年数学建模竞赛汇总——时间轴

美赛已过&#xff0c;好多小伙伴表示已经错过&#xff0c;不清楚什么时候报名&#xff0c;什么时候准备&#xff0c;其实每年数学建模比赛有很多个&#xff0c;各大比赛的级别、报名时间、参赛对象等要求什么呢&#xff1f;小编从竞赛说明、竞赛级别、是否允许跨校、报名费用、…

MySQL基础学习

MySQL基础 注意&#xff1a;本文的图片截图自尚硅谷MySQL笔记。 一&#xff1a;基本概述&#xff1a; 什么是数据库&#xff1a; 数据库是一种用来存储和管理数据的系统。它是一个组织化的数据集合&#xff0c;可以通过计算机系统进行访问、管理和更新。数据库可以存储各种…

【C++】vector模拟实现+迭代器失效

vector模拟实现 成员变量定义默认成员函数构造函数 迭代器范围for、对象类型匹配原则 容量操作sizeemptycapacityreserve成员变量未更新memcpy值拷贝 resize内置类型的构造函数 数据访问frontbackoperator[ ] 数据修改操作push_backpop_backswapclearinsertpos位置未更新无返回…

Atcoder ABC340 C - Divide and Divide

Divide and Divide&#xff08;分而治之&#xff09; 时间限制&#xff1a;2s 内存限制&#xff1a;1024MB 【原题地址】 所有图片源自Atcoder&#xff0c;题目译文源自脚本Atcoder Better! 点击此处跳转至原题 【问题描述】 【输入格式】 【输出格式】 【样例1】 【样例…

Unity2023.1.19没有PBR Graph?

Unity2023.1.19没有PBR Graph? 关于Unity2023.1.19没有PBR graph的说法,我没看见管方给出的答案,百度则提到了Unity2020版之后Shader Graph的“全新更新”,之前也没太注意版本的区别,以后项目尽量都留心一下。 之前文章说过,孪生智慧项目推荐使用URP渲染管线,以上的截…

基于学习的参数化查询优化方法

一、背景介绍 参数化查询是指具有相同模板&#xff0c;且只有谓词绑定参数值不同的一类查询&#xff0c;它们被广泛应用在现代数据库应用程序中。它们存在反复执行动作&#xff0c;这为其性能优化提供了契机。 然而&#xff0c;当前许多商业数据库处理参数化查询的方法仅仅只…

JavaScript编写一个倒计时

关键js代码 <script>function countdown() {var targetDate new Date("2024-02-20 12:00:00");var currentDate new Date();var timeDiff targetDate.getTime() - currentDate.getTime();var days Math.floor(timeDiff / (1000 * 60 * 60 * 24));var hours…

电商数据API接口 | 节省你的电商数据采集成本

1、将数据采集的整体成本降低55%。在电商API接口负责了整个数据采集流程后&#xff0c;这家电商公司成功节约了维护和开发上的成本。 2、电商爬虫API可以从极复杂的来源中采集数据&#xff0c;确保完整交付。在电商爬虫API的帮助下&#xff0c;该公司现在可以获取完成业务目标所…