2023-01-17 PostgreSQL 并行查询概述

news2024/11/25 16:26:22

简介: 大数据时代,人们使用数据库系统处理的数据量越来越大,请求越来越复杂,对数据库系统的大数据处理能力和混合负载能力提出更高的要求。PostgreSQL 作为世界上最先进的开源数据库,在大数据处理方面做了很多工作,如并行和分区。

大数据时代,人们使用数据库系统处理的数据量越来越大,请求越来越复杂,对数据库系统的大数据处理能力和混合负载能力提出更高的要求。PostgreSQL 作为世界上最先进的开源数据库,在大数据处理方面做了很多工作,如并行和分区。

PostgreSQL 从 2016 年发布的 9.6 开始支持并行,在此之前,PostgreSQL 仅能使用一个进程处理用户的请求,无法充分利用资源,亦无法很好地满足大数据量、复杂查询下的性能需求。2018 年 10 月发布的 PostgreSQL 11,在并行方面做了大量工作,支持了并行哈希连接,并行 Append 以及并行创建索引等特性,对于分区表,支持了 Partition-wise JOIN

本文从以下三方面介绍 PostgreSQL 的并行查询特性:

  • 并行查询基础组件,包括后台工作进程(Background Work Process),动态共享内存(Dynamic Shared Memory)以及后台工作进程间的通信机制和消息传递机制
  • 并行执行算子的实现,包括并行顺序扫描、并行索引扫描等并行扫描算子,三种连接方式的并行执行以及并行 Append
  • 并行查询优化,介绍并行查询引入的两种计划节点,基于规则计算后台工作进程数量以及代价估算

举个例子

首先,通过一个例子,让我们对 PostgreSQL 的并行查询以及并行计划有一个较宏观的认识。如下查询:统计人员表 people 中参加 2018 PostgreSQL 大会的人数:

SELECT COUNT(*) FROM people WHERE  inpgconn2018 = 'Y';

没有开并行的情况下(max_parallel_workers_per_gather=0),查询计划如下:

Aggregate  (cost=169324.73..169324.74 rows=1 width=8) (actual time=983.729..983.730 rows=1 loops=1)
   ->  Seq Scan on people  (cost=0.00..169307.23 rows=7001 width=0) (actual time=981.723..983.051 rows=9999 loops=1)
         Filter: (atpgconn2018 = 'Y'::bpchar)
         Rows Removed by Filter: 9990001
 Planning Time: 0.066 ms
 Execution Time: 983.760 ms

开启并行的情况下(max_parallel_workers_per_gather=2),查询计划如下:

Finalize Aggregate  (cost=97389.77..97389.78 rows=1 width=8) (actual time=384.848..384.848 rows=1 loops=1)
   ->  Gather  (cost=97389.55..97389.76 rows=2 width=8) (actual time=384.708..386.486 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=96389.55..96389.56 rows=1 width=8) (actual time=379.597..379.597 rows=1 loops=3)
               ->  Parallel Seq Scan on people  (cost=0.00..96382.26 rows=2917 width=0)
                 (actual time=378.831..379.341 rows=3333 loops=3)
                     Filter: (atpgconn2018 = 'Y'::bpchar)
                     Rows Removed by Filter: 3330000
 Planning Time: 0.063 ms
 Execution Time: 386.532 ms

max_parallel_workers_per_gather 参数控制执行节点的最大并行进程数,通过以上并行计划可知,开启并行后,会启动两个 worker 进程(即 Workers Launched: 2)并行执行,且执行时间(Execution Time)仅为不并行的40%。该并行计划可用下图表示:

并行查询计划中,我们将处理用户请求的 backend 进程称之为主进程(leader),将执行时动态生成的进程称之为工作进程(worker)。每个 worker 执行 Gather 节点以下计划的一个副本,leader 节点主要负责处理 Gather 及其以上节点的操作,根据 worker 数不同,leader 也可能会执行 Gather 以下计划的副本。

并行基础组件

PostgreSQL 从 9.4 和 9.5 已经开始逐步支持并行查询的一些基础组件,如后台工作进程,动态共享内存和工作进程间的通信机制,本节对这些基础组件做简要介绍。

后台工作进程(Background Worker Process)

PostgreSQL 是多进程架构,主要包括以下几类进程:

  • 守护进程,通常称之为 postmaster 进程,接收用户的连接并 fork 一个子进程处理用户的请求
  • backend 进程,即 postmaster 创建的用于处理用户请求的进程,每个连接对应一个 backend 进程
  • 辅助进程,用于执行 checkpoint,后台刷脏等操作的进程
  • 后台工作进程,用于执行特定任务而动态启动的进程,如上文提到的 worker 进程

上图中 server process 即 postmaster 进程,在内核中,postmaster 与 backend 进程都是 postgres 进程,只是角色不同。对于一个并行查询,其创建 worker 进程的大致流程如下:

  1. client 创建连接,postmaster 为其 fork 一个 backend 进程处理请求
  2. backend 接收用户请求,并生成并行查询计划
  3. 执行器向 backgroudworker 注册 worker 进程(并没有启动)
  4. 执行器通知(kill)postmaster 启动 worker 进程
  5. worker 进程与 leader 进程协调执行,将结果返回 client

动态共享内存(Dynamic Shared Memory)与 IPC

PostgreSQL 是多进程架构,进程间通信往往通过共享内存和信号量来实现。对于并行查询而言,执行时创建的 worker 进程与 leader 进程同样通过共享内存实现数据交互。但这部分内存无法像普通的共享内存那样在系统启动时预先分配,毕竟直到真正执行时才知道有多少 worker 进程,以及需要分配多少内存。

PostgreSQL 实现了动态共享内存,即在执行时动态创建,用于 leader 与 worker 间通信,执行完成后释放。基于动态共享内存的队列用于进程间传递元组和错误消息。

如下图,每个 worker 在动态共享内存中都有对应的元组队列和错误队列,worker 将执行结果放入队列中,leader 会从对应队列获取元组返回给上层算子。动态共享内存的具体实现原理和细节在此不做展开。

并行执行

以上简单介绍了并行查询依赖的两个重要基础组件:后台工作进程和动态共享内存。前者用于动态创建 worker,以并行执行子查询计划;后者用于 leader 和 worker 间通信和数据交互。本节介绍 PostgreSQL 目前支持并行执行的算子的实现原理,包括:

  • 并行扫描,如并行顺序扫描,并行索引扫描等
  • 并行连接,如并行哈希连接,并行 NestLoop 连接等
  • 并行 Append

并行扫描

并行扫描的理念很朴素,即启动多个 worker 并行扫描表中的数据。以前一个进程做所有的事情,无人争抢,也无需配合,如今多个 worker 并行扫描,首先需要解决如何分工的问题。

PostgreSQL 中的并行扫描分配策略也很直观,即 block-by-block。多个进程间(leader 和 worker)维护一个全局指针 next,指向下一个需要扫描的 block,一旦某个进程需要获取一个 block,则访问该指针,获取 block 并将指针向前移动。

目前支持并行的常用扫描算子有:SeqScanIndexScanBitmapHeapScan 以及 IndexOnlyScan

下图分别是并行 SeqScan(左)和 并行 IndexScan(右)的原理示意图,可见两者均维护一个 next 指针,不同的是 SeqScan 指向下一个需要扫描的 block,而 IndexScan 指向下一个索引叶子节点。

注意,目前并行 IndexScan 仅支持 B-tree 索引。

并行 IndexOnlyScan 的原理类似,只是无需根据索引页去查询数据页,从索引页中即可获取到需要的数据;并行 BitmapHeapScan 同样维护一个 next 指针,从下层 BitmapIndexScan 节点构成的位图中依次分配需要扫描的 block。

并行连接

PostgreSQL 支持三种连接算法:NestLoopMergeJoin 以及 HashJoin。其中 NestLoop 和 MergeJoin 仅支持左表并行扫描,右表无法使用并行;PostgreSQL 11 之前 HashJoin 也仅支持左表并行,PostgreSQL 11 支持了真正的并行 HashJoin,即左右表均可以并行扫描。

以下图左侧的 NestLoop 查询计划为例,NestLoop 左表是并行 Seq Scan,右表是普通的 Index Scan,三个进程(1 个 leader,2 个 worker)分别从左表中并行获取部分数据与右表全量数据做 JOIN。Gather 算子则将子计划的结果向上层算子输出。图中右表是索引扫描,其效率可能还不错,如果右表是全表扫描,则每个进程均需要全表扫描右表。

同理,MergeJoin 也是类似的,左表可以并行扫描,右表不能并行。由于 MergeJoin 要求输入有序,如果右侧计划需要显式排序,则每个进程都需要执行 sort 操作,代价较高,效率较低。

PostgreSQL 10 中的并行 HashJoin 如下图所示,每个子进程都需要扫描右表并构建各自的 HashTable 用于做 HashJoin

PostgreSQL 11 实现了真正的并行 HashJoin,所有进程并行扫描右表并构建共享的 HashTable,然后各进程并行扫描左表并执行 HashJoin,避免了 PostgreSQL 10 中各自构建一个私有 HashTable 的开销。

关于三种 HashJoin 的执行效率以及性能提升,读者可以参考 THOMAS MUNRO 的这篇文章。

并行 Append

PostgreSQL 中用 Append 算子表示将多个输入汇聚成一个的操作,往往对应 SQL 语法中的 UNION ALL。在 PostgreSQL 11 中实现了 partition-wise join,如果多个分区表的查询满足特定连接条件(如拆分键上的等值连接),则可将其转换为多个子分区的局部 JOIN,然后再将局部 JOIN 的结果 UNION ALL 起来。具体转换细节以及实现在此不展开,读者可以参考 Ashutosh Bapat (आशुतोष बापट) 的这篇文章。以下给出一个转换后的示例图:

在实现并行 Append 之前,Append 算子下的多个孩子节点均只能通过一个进程依次执行,并行 Append 则分配多个 worker 进程,并发执行多个孩子节点,其孩子节点可以是以上介绍的并行执行算子,也可以是普通的算子。

并行查询优化

PostgreSQL 实现了基于代价的优化器,大致流程如下:

  1. 考虑执行查询的可能路径(Path)
  2. 估算代价,并选择代价最小的路径
  3. 将路径转换为计划供执行器执行

在并行查询优化中,将路径节点分为两类:

  • parallel-aware 节点,即节点本身可以感知自己在并行执行的节点,如 Parallel Seq Scan
  • parallel-oblivious 节点,即节点本身意识不到自己在并行执行,但也可能出现在并行执行的子计划中(Gather 以下),如以上提到的并行 NestLoop 计划中的 NestLoop 节点

并行查询引入了两个新的节点:Gather 和 GatherMerge,前者将并行执行子计划的结果向上层节点输出,不保证有序,后者能够保证输出的顺序。

并行查询优化在生成路径时,会生成部分路径(Partial Paths),所谓 partial,即说明该路径仅处理部分数据,而非全部数据。Partial Paths 从最底层的扫描节点开始,比如 Parallel Seq Scan,就是一个 partial path;包含 partial path 的路径(Gather/GatherMerge 以下)同样也是 partial path,比如我们在 Partial Seq Scan 节点上做聚合操作(Aggregate),此时的聚合操作是对局部数据的聚合,即 Partial Aggregate。随后,优化器会在 partial path 之上添加对应的 Gather/GatherMerge 节点,Gather/GatherMerge 相当于把 partial path 封装成一个整体,对上屏蔽并行的细节。

并行度

既然要并行执行,就需要解决并行度的问题,即评估需要几个 worker。目前,PostgreSQL 仅实现了基于规则的并行度计算,大体包括两部分:

  • 通过 GUC 参数获取并行度
  • 基于表大小评估并行度

并行度相关的 GUC 参数如下:

  • max_parallel_workers_per_gather 每个 Gather/GatherMerge 最大的并行 worker 数(不包含 leader)
  • force_parallel_mode 是否强制使用并行
  • min_parallel_table_scan_size 使用并行扫描的最小表大小,默认 8MB
  • min_parallel_index_scan_size 使用并行扫描的最小索引大小,默认 512KB

根据表大小计算并行度的公式如下:

 log(x / min_parallel_table_scan_size) / log(3) + 1 workers

以 min_parallel_table_scan_size 为默认值 8MB 来计算,表大小在 [8MB, 24MB) 区间时为 1 个 worker,在 [24MB, 72MB) 区间时为 2 个 worker,以此类推。

需要注意的是,尽管在查询优化阶段已经计算了并行度,但最终执行的时候是否会启动对应数量的进程还取决于其他的因素,如最大允许的后台工作进程数(max_worker_processes),最大允许的并行进程数(max_parallel_workers),以及事务隔离级别是否为 serializable(事务隔离级别可能在查询优化以后,真正执行之前发生改变)。一旦无法启动后台工作进程,则由 leader 进程负责运行,即退化为单进程模式。

代价估算

并行查询优化需要估算 Partial Path 的代价以及新加节点 Gather/GatherMerge 的代价。

Partial Path

对于 Partial Path 中的 parallel-aware 节点,比如 Partial Seq Scan,由于多个 worker 并行扫描,每个 worker 处理的数据量减少,CPU 消耗也减少,通过如下方法评估 parallel-aware 的 CPU 代价和处理行数。

  1. 计算并行除数(parallel_divisor)
double        parallel_divisor = path->parallel_workers;
if (parallel_leader_participation)
{
        double        leader_contribution;
    
        leader_contribution = 1.0 - (0.3 * path->parallel_workers);
        if (leader_contribution > 0)
            parallel_divisor += leader_contribution;
}
以上算法说明,worker 越多,leader 就越少参与执行 `Gather/GatherMerge` 以下的子计划,一旦 worker 数超过 3 个,则 leader 就完全不执行子计划。其中 `parallel_leader_participation` 是一个 GUC 参数,用户可以显式控制是否需要 leader 参与子计划的执行。
  1. 估算 CPU 代价,即 cpu_run_cost /= parallel_divisor
  2. 估算行数,path->rows / parallel_divisor

对于 Partial Path 中的 parallel-oblivious 节点,则无需额外处理,由于其并不感知自身是否并行,其代价只需要根据下层节点的输入评估即可。

Gather/GatherMerge

并行查询中引入了两个新的代价值:parallel_tuple_cost 和 parallel_setup_cost

  • parallel_tuple_cost 每个 Tuple 从 worker 传递给 master 的代价,即 worker 将一个 tuple 放入共享内存队列,然后 master 从中读取的代价,默认值为 0.1
  • parallel_setup_cost 启动并行查询 worker 进程的代价,默认值为 1000

在此不具体介绍这两个节点的代价计算方式,感兴趣的读者可以参考 cost_gather 和 cost_gather_merge 的实现。

并行限制

PostgreSQL 并行查询功能日趋完善,但仍然有很多情况不支持使用并行,这也是未来社区需要解决的问题,主要包括以下场景:

  • 任何写数据或者锁行的查询均不支持并行,CREATE TABLE ... ASSELECT INTO,和 CREATE MATERIALIZED VIEW 等创建新表的命令可以并行
  • 包含 CTE(with...)语句的查询不支持并行
  • DECLARE CURSOR 不支持并行
  • 包含 PARALLEL UNSAFE 函数的查询不支持并行
  • 事务隔离级别为 serializable 时不支持并行

更多的并行限制,读者可以参考官网。

总结

本文从并行基础组件、并行执行以及并行查询优化三方面介绍了 PostgreSQL 的并行查询特性,每个模块的介绍都较为宏观,不涉及太多实现细节。希望读者可以借此了解 PostgreSQL 并行查询的全貌,对实现细节感兴趣的读者亦可以此为指引,深入解读源码,加深理解。

当然,PostgreSQL 并行特性涉及模块众多,实现复杂,笔者对其理解也还有很多不到位的地方,还望大家多多指正。

参考文献

  • PostgreSQL: Documentation: 11: Chapter 15. Parallel Query
  • https://speakerdeck.com/macdice/parallelism-in-postgresql-11
  • PostgreSQL: Documentation: 11: 15.3. Parallel Plans
  • http://rhaas.blogspot.com/2013/10/parallelism-progress.html
  • Parallel Hash for PostgreSQL
  • https://write-skew.blogspot.com/2018/01/parallel-hash-for-postgresql.html
  • http://amitkapila16.blogspot.com/2015/11/parallel-sequential-scans-in-play.html
  • Parallel Aggregate - Getting the most out of your CPUs - 2ndQuadrant | PostgreSQL
  • https://www.pgcon.org/2017/schedule/attachments/445_Next-Generation%20Parallel%20Query%20-%20PGCon.pdf
  • Parallelism and Partitioning - Improvements in Postgres 11

PostgreSQL 并行查询概述-阿里云开发者社区

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

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

相关文章

详谈ORB-SLAM2的单目初始化器Initializer

单目初始化器Initializer类,这个类只用于单目初始化,因为这是ORB-SLAM里遗留的一个类,也是祖传代码,双目和RGBD相机只需要一帧就能初始化,因为双目和RGBD相机拍到的点都是有信息的,但是单目相机就不一定了&…

六种方法在云平台和远程桌面中使用Kali

一、说明 本篇主要介绍方便在云服务器,或者以远程桌面(GUI)形式使用kali配置教程,帮助渗透更加方便顺利。 二、方法 2.1 方法一 云服务提供商预装 备注:预算充足,可以首考虑此方法 优点: 云服…

java 探花交友项目实战 day3 完善个人信息 阿里云OSS文件存储 百度人脸识别

完善用户信息 业务概述 阿里云OSS Data ConfigurationProperties(prefix "tanhua.oss") public class OssProperties { private String accessKey; private String secret; private String bucketName; private String url; //域名 private Strin…

微分方程的特征值解法:斯图姆-刘维尔方程

一.基础概念 前置:福克斯定理和奇点理论 常点的级数解 奇异点的级数解 则至少存在一个如下形式的解(弗罗贝尼乌斯级数): 19世纪中期,常微分方程的研究到了新的阶段,存在定理和斯图姆-刘维尔理论都假设微分方程区域内含解析函数或至少包含连续函数,而另一方面,以前研究…

东莞注塑MES管理系统具有哪些功能

伴随着人们对于物质生活的品质要求越来越高,日用品、医疗保健、汽车工业、电子行业、新能源、家电、包装行业以及建筑等行业对注塑产品的需求量日益突出。注塑企业提供的各种各样的塑料产品已渗透到经济生活的各个领域,为国家经济的各个部门包括轻工业和…

ARM SD卡启动详解

一、主流的外存设备介绍 内存和外存的区别:一般是把这种 RAM(random access memory,随机访问存储器,特点是任意字节读写,掉电丢失)叫内存,把 ROM(read only memory,只读存储器,类似…

15子空间投影

子空间投影 从向量的投影入手,延伸到高维投影,并将投影使用矩阵形式给出。做投影也即向另一个向量上做垂线。上一章讨论的Axb无解时的最优解求解时,并没有解释这个最优解为何“最优”,本节课给出相应的解释。相对简单的二维空间的…

MyBatis -- resultType 和 resultMap

MyBatis -- resultType 和 resultMap一、返回类型&#xff1a;resultType二、返回字典映射&#xff1a;resultMap一、返回类型&#xff1a;resultType 绝⼤数查询场景可以使用 resultType 进⾏返回&#xff0c;如下代码所示&#xff1a; <select id"getNameById"…

企业如何借助制造业ERP系统,做好生产排产管理?

随着市场竞争越来越激烈&#xff0c;生产制造行业订单零碎化趋势越发突出。面对品种多&#xff0c;数量小&#xff0c;批次多&#xff0c;个性化需求也多的生产方式&#xff0c;PMC生产排产管理变得非常困难&#xff1b;同时生产过程还会有各种不确定的临时性因素出现&#xff…

详解pandas的read_csv函数

一、官网参数 pandas官网参数网址&#xff1a;pandas.read_csv — pandas 1.5.2 documentation 如下所示&#xff1a; 二、常用参数详解 1、filepath_or_buffer(文件) 一般指读取文件的路径。比如读取csv文件。【必须指定】 import pandas as pddf_1 pd.read_csv(r"C:…

Xilinx FPGA电源设计与注意事项

1 引言随着半导体和芯片技术的飞速发展&#xff0c;现在的FPGA集成了越来越多的可配置逻辑资源、各种各样的外部总线接口以及丰富的内部RAM资源&#xff0c;使其在国防、医疗、消费电子等领域得到了越来越广泛的应用。当采用FPGA进行设计电路时&#xff0c;大多数FPGA对上电的电…

软件测试复习06:基于经验的测试

作者&#xff1a;非妃是公主 专栏&#xff1a;《软件测试》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录软件缺陷基于缺陷分类的测试缺陷模式探索性测试软件缺陷 主要由以下几种原因造成&#xff1a; 疏…

Redux相关知识(什么是redux、redux的工作原理、redux的核心概念、redux的基本使用)(十一)

系列文章目录 第一章&#xff1a;React基础知识&#xff08;React基本使用、JSX语法、React模块化与组件化&#xff09;&#xff08;一&#xff09; 第二章&#xff1a;React基础知识&#xff08;组件实例三大核心属性state、props、refs&#xff09;&#xff08;二&#xff0…

Arduino 开发ESP8266(ESP12F)模块

①ESP12F模块的硬件说明如上图所示&#xff0c;其他引脚均引出。②准备好硬件之后就是要下载Arduino IDE&#xff0c;目前版本为2.0.3&#xff0c;下载地址为&#xff1a;https://www.arduino.cc/en/software&#xff0c;如下图所示③安装Arduino IDE较为简单&#xff0c;安装之…

aws cloudformation 在堆栈中使用 waitcondition 协调资源创建和相关操作

参考资料 https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-waitcondition.htmlhttps://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-waitcondition.html 本文介绍cloudformation的waitcondition条件&#xff0c;wait…

Win10之bandicam录音无声音问题

0.问题描述&#xff1a;在Xubuntu22.04中通过gnome-boxes跑win10&#xff0c;但是win10本机录音机录音ok&#xff0c;使用bandicam录屏却没声音的问题&#xff0c;以下是分析步骤。1.Linux端设置选择Xbuntu声音图标speaker选择声卡&#xff1a;sof-hda-dsp Speaker Headphonesm…

DFS剪枝

目录 一、前言 二、剪枝 1、概念 2、类别 三、例题 1、剪格子&#xff08;lanqiaoOJ题号211&#xff09; 2、路径之谜&#xff08;2016年决赛&#xff0c;lanqiaoOJ题号89&#xff09; 3、四阶幻方&#xff08;2015年决赛&#xff0c;lanqiaoOJ题号689&#xff09; 4、…

P1028 [NOIP2001 普及组] 数的计算————C++

题目 [NOIP2001 普及组] 数的计算 题目描述 给出自然数 nnn&#xff0c;要求按如下方式构造数列&#xff1a; 只有一个数字 nnn 的数列是一个合法的数列。在一个合法的数列的末尾加入一个自然数&#xff0c;但是这个自然数不能超过该数列最后一项的一半&#xff0c;可以得到…

linux(debian系列)配置seetaface6

seetaface6依赖于opencv&#xff0c;另外我们需要界面&#xff0c;所以也需要Qt&#xff08;你也可以选择其他的&#xff09;。 这里的目标是配置好环境&#xff0c;能够编译并运行seetaface6给的demo。 那个demo中用到了sqlite数据库&#xff0c;所以我们还需要安装sqlite。…

Cosmos 基础(一)

Cosmos 区块链互联网 Cosmos是一个不断扩展的生态系统&#xff0c;由相互连接的应用程序和服务组成&#xff0c;为去中心化的未来而构建。 Cosmos 应用程序和服务使用IBC(the Inter-Blockchain Communication protocol, 区块链间通信协议)连接。这一创新使您能够在主权国家之…