PgSQL · 答疑解惑 · PostgreSQL 9.6 并行查询实现分析

背景

随着PG9.5 项目的release,属于PG9.6的代码也陆续进入代码主干,其中最让人激动的特性并行查询终于进入了核心代码。pger们对这个新特性期待了太久的时间,代码刚提交我们就迫不及待的拿到,从设计到性能进行一番探究,并通过本文介绍给大家。

并行技术的过去和未来

这是个很困难的工作,要说清楚它需要讲清楚并行技术相关的一些背景。

PG 目前的架构是基于多进程的,必要的信息通过共享内存这样的机制来传递。
该架构的好处是:

  1. 代码相对简单;
  2. 在多CPU环境下多会话任务可以由操作系统来调度;
  3. 多进程程序相对稳定。

不幸的是,虽然多会话任务可以利用操作系统并行调度满足需求,但是单个任务却只能最多使用一个CPU和一个IO通道。最近的计算机架构发展呈现出这样的发展趋势

  1. 单个CPU的运算能力没有大增长;
  2. 越来越多的CPU核心;
  3. SSD 存储的崛起 I/O 延迟急剧降低(尤其随机读写)。

但是数据库要处理的单个会话任务的复杂程度却急剧增加(想想复杂的多表JOIN任务、大表扫描任务、聚合操作和大量数据排序任务,再想想OLAP报表SQL)。单个任务的处理能力越来越成为了数据库任务处理的瓶颈.

PG发展路线是相对保守的,即使在这样的趋势下,已经在多个方面使用了并行技术。

  1. 利用部分OS系统调度的并行IO调度(effective_io_concurrency,已完成);
  2. 并行逻辑备份和恢复技术(已完成);
  3. 并行执行器(in pg96)。

对于并行执行器,也就是本文讨论的内容,相对于其他的技术点难度显然大得多。
对于目前的架构,单个SQL任务的执行被明显的分为:

  1. 语法分析语义识别;
  2. 查询重写;
  3. 产生查询计划;
  4. 执行查询计划。

一共4个大的阶段。并行技术很难把这几个明显的阶段并行起来执行,也不可能把某一阶段的工作提前。但是把执行查询计划这个阶段并行起来是可能的,也就是并行执行器。

设计思路

整个设计可以分为下面几个大的部分

  1. 一套用于并行执行框架的基础设施
    包括容错机制,这部分工作涉及到的点很杂也很多,按照计划在PG9.5之前就已经实现了其中的大部分。其中很重要的是容错机制,主进程需要了解属于自己工作进程的执行状态,处理工作进程执行过程中发生的任何错误,还有动态工作进程,动态共享内存API等等工作进程消息处理。
  2. 修改优化器,在传统的代价模型基础上增加计算并行执行路径的的代价数据,优化器能够输出并行执行计划。增加并行执行节点相关的path、plan,用于存放并行相关的代价信息。
  3. 开发一套用于多进程间同步数据的机制,目前的实现是开辟共享内存。当然也有其他选择,发送和接受数据的格式和形式也需要设计。
  4. 动态启动多个工作进程,把查询计划中部分任务下发给它们执行
    需要重组目前传统的执行器流程,也就是在目前执行器上面添加用户并行处理的执行节点:1)并行扫描节点,2)数据发送接收节点。

结合代码进行说明

还是从设计思路的4个方面讲。

基础设施
如上面的描述,这部分相当的杂,这些都是实现并行执行的技术设施。下面列举主要的部分:

  1. 动态共享内存,9.4完成,并提供了几种底层的实现选择(不同的OS选择不同),参考参数 dynamic_shared_memory_type
  2. 共享内存消息队列shm_mq,用于通过共享内存传递数据和状态。通过核心函数 shm_mq_receive ,可以看到无论是数据还是错误消息都通过该机制来同步;
  3. 主进程同步给工作进程相关的各种会话信息
    • 动态库 RestoreLibraryState()
    • 用户信息,用户登录的DB BackgroundWorkerInitializeConnectionByOid()
    • 当前会话中的GUC参数,并行SQL所在的事务信合和状态 RestoreGUCState()
    • 快照信息 RestoreSnapshot()
    • ComboCID信息 RestoreComboCIDState()

可以看到,为了让工作进程完成部分工作,需要装载主进程的很多上下文信息。这里有大量的工作,也意味着并行模式需要承担一定的代价。这一点PG的并行模型的代价模型实现中有清晰的考虑。

完成了这一步,才能重用目前执行器中的大量现有流程。

修改优化器
考虑优化器的相应修改,我们知道PG优化器生成执行计划是基于代价模型,并行执行在优化器的重点就是考虑如何准确估计并行执行的代价。

实现原理:新增并行相关的节点的执行path,并填充他们准确的 cost,让它们参与到动态规划或遗传算法的迭代计算中。最终如果并行相关path最优,则创建完整的执行计划交给执行器执行.

  1. 新增的cost类型

    • parallel_setup_cost 并行计划启动代价,对应工作进程的创建和上下文信息的传递所需要的代价。它也说明只有需要一定工作量的复杂SQL才有必要使用并行方式执行;
    • parallel_tuple_cost 主进程和工作进程间传递数据是需要消耗资源的,这取决于实现它的方式(目前消耗的资源多是内存拷贝和tuple的重组和解析);
      上述代价是并行执行模型需要考虑的,结合统计信息中表上的其他信息,能预估出对应表或JOIN使用并行模型执行时的代价。
  2. cost_seqscan 顺序扫描采取了并行的执行方式,需要计算并行模式的代价。
    顺序扫描的代价分为3个部分 startup_cost + cpu_run_cost + disk_run_cost 并行模式下CPU 和 DISK 被分担到了多个工作进程中,每个工作进程处理整个表中的一部分数据。相应的代价被重新评估.

    • create_parallel_paths 适合并行的表创建并行path并,并填充cost;
    • standard_planner 当然并行模式并不适合所有查询,做逻辑优化阶段需要关掉并行计划的计算;
    • 当然,随着工作进程能承担的工作越多,更多的执行节点可以让工作进程完成,在优化器中需要做适当的节点下推(push down)。

数据同步
这部分(shm_mq)底层使用共享内存在一个OS中,在多个独立进程间同步数据。在实现上又抽象成了消息队列的形式,用于工作进程和主进程间同步数据。

表上的数据(tuple)和错误消息被封装成”消息”的形式发送给主进程,核心函数shm_mq_sendv 和shm_mq_receive 可以看到,底层实现是通过在共享内存上用memcpy来做的。

执行流程重组
执行器的工作主要是改造传统的逐层迭代方式以支持并行执行方式,当然是在重用之前代码的基础上,几个关键的实现是:

  1. ExecGather 添加用于接受工作进程发送数据的节点,内部调用了底层shm_mq 模块中的API;
  2. 在工作进程空间中,添加流程 ParallelQueryMain 用于工作进程完成工作并把数据通过 shm_mq 发送给主进程;
  3. 改造顺序扫描执行节点和下层的存取节点,支持按照blocknum为单位并行扫描同一个表。核心函数 heap_parallelscan_nextpage,他决定当前工作进程扫描任务是如何分配的。

该部分的工作重用了大量的旧的流程,但这和之前的执行器的工作模式有本质的区别,大量任务在独立的进程空间中由OS 并行的调度执行,它们用 shm_mq 传递数据。

总结

从公布的测试数据上来,部分场景在并行模式下能显著提高性能。

由于并行模式有一定的开销(被抽象成了各种成本),它并不是万金油。当然,好的实现能让它适应更多更复杂的场景。数据量特别小的场景不适合使用并行,这一点优化器能很好的评估成本,选择正确的执行计划。

其次,并行工作进程并不是越多越好,多到一定程度后性能的提升就不明显了。

目前能放在工作进程中并行执行的任务还不多,只支持扫描类型的任务,但是整个并行框架是有了基本的雏形,相信几轮迭代之后整套执行框架会越来越高效和稳定。

时间: 2024-12-29 13:47:57

PgSQL · 答疑解惑 · PostgreSQL 9.6 并行查询实现分析的相关文章

PostgreSQL · 实现分析 · PostgreSQL 10.0 并行查询和外部表的结合

title: PostgreSQL · 实现分析 · PostgreSQL 10.0 并行查询和外部表的结合 author: 义从 前言 大家都知道,PostgreSQL 近几大版本中加入了很多 OLAP 相关特性.9.6 的并行扫描应该算最大的相关特性.在今年发布的 10.0 中,并行扫描也在不断加强,新增了并行的索引扫描. 我们知道并行扫描是支持外部数据源的.在云上,有很多存储存储产品可以以外部数据源的形式做数据库的外部存储.例如,阿里云的 OSS 和 AWS 的 S3 都是绝佳的外部数据源

PgSQL · 答疑解惑 · 表膨胀

背景 最近处理了几起线上实例表膨胀的问题.表膨胀是指表的数据和索引所占文件系统的空间,在有效数据量并未发生大的变化的情况下,不断增大.PG使用过程中需要特别关注这方面,我们来给大家解析一下表膨胀的原因. 表膨胀的直接触发因素是表上的大量更新,如全表的update操作.大量的insert+delete操作等.而我们知道,PG在更新数据时,是不直接删除老数据的.一个update操作执行后,被更改的数据的旧版本也被保留下来,直到对表做vacuum操作的时候,才考虑回收旧版本.做数据更新时,这些旧版本不

数据库内核月报 - 2015 / 11-PgSQL · 答疑解惑 · PostgreSQL 用户组权限管理

背景 RDS上的PG没有开放超级用户,这给很多云上的客户使用PG带来了困难.因此有必要给大家讲讲PG的用户权限管理的一些小知识,它可以很好的帮助用户顺利的从之前的 DB 管理方式过度到云上. PG 的 superuser 拥有几乎全部的数据库权限,甚至可以直接修改系统表,潜在风险相当大: RDS PG 使用 superuser 运维 DB,例如管理用.管理流复制.备份等,这些操作用户是不需要关心的,换句话说它应该完全的交给云服务来处理: 对于用户而言,PG 的普通用户权限是完全够用的.使用普通用

PostgreSQL SQL 语言:并行查询

本文档为PostgreSQL 9.6.0文档,本转载已得到原译者彭煜玮授权. 1. 并行查询如何工作 当优化器判断对于某一个特定的查询,并行查询是最快的执行策略时,优化器将创建一个查询计划.该计划包括一个 Gather 节点.下面是一个简单的例子: EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%'; QUERY PLAN --------------------------------------------------

实战 Eclipse ,Jigloo, PostgreSQL,JDBC 开发数据库查询应用系统起步

数据|数据库 实战 Eclipse ,Jigloo, PostgreSQL,JDBC 开发数据库查询应用系统起步 1 安装 Eclipse笔者用的GNU/Linux先从 下载了些GTK+相关的包编译安装之后,到Eclipse主页上 找到了Linux下GTK的 Eclipse安装文件 2 .插件笔者安装了两个插件一个是GUI设计工具 jigloo,主页:http://cloudgarden.com/jigloo/ 一个是打包工具 fat jar exporterhttp://fjep.source

用Oracle并行查询发挥多CPU的威力

在一个单独的服务器中安装更多的CPU成为目前的一个趋势.使用对称多处理服务器(SMP)的情况下,一个Oracle服务器拥有8个.16个或32个CPU以及几吉比特RAM的SGA都不足为奇.     Oracle跟上了硬件发展的步伐,提供了很多面向多CPU的功能.从Oracle8i开始,Oracle在每个数据库函数中都实现了并行性,包括SQL访问(全表检索).并行数据操作和并行恢复.对于Oracle专业版的挑战是为用户的数据库配置尽可能多的CPU.     在Oracle环境中实现并行性最好的方法之

并行查询让SQL Server加速运行

并行查询其优势就是可以通过多个线程来处理查询作业,从而提高查询的效率.SQL Server数据库为具有多个CPU的数据库服务器提供并行查询的功能,以优化查询作业的性能.也就是说,只要数据库服务器有多个CPU,则数据库系统就可以使用多个操作系统进程并行执行查询操作,来加速完成查询作业. 一.并行查询三步走. 并行查询作业在数据库中,主要经过三个步骤. 首先,数据库会判断是否需要进行并行查询.在数据库中有一个查询优化器,会对SQL语句进行优化,然后数据库才会去执行查询语句.而这个查询器在对SQL语句

postgresql java问题-Postgresql java编程用jdbc连接,怎么样执行postgresql中 d 的查询指令

问题描述 Postgresql java编程用jdbc连接,怎么样执行postgresql中 d 的查询指令 请各位大神帮忙解决一下这个问题:"Postgresql java编程用jdbc连接,怎么样执行postgresql中 d 的查询指令"谢谢. 情况是这样: 我要把所有表的constraints的column找出来,但是我只能通过pg_indexes找到constraints的名字,只有通过d constraints_name 才能找到column,所有请教各位大神有没有解决方法

用Oracle并行查询发挥多CPU的威力_oracle

正在看的ORACLE教程是:用Oracle并行查询发挥多CPU的威力.参数  让我们进一步看看CPU的数量是如何影响这些参数的. 参数fast_start_parallel_rollback Oracle并行机制中一个令人兴奋之处是在系统崩溃时调用并行回滚得能力.当Oracle数据库发生少有的崩溃时,Oracle能自动检测未完成的事务并回滚到起始状态.这被称为并行热启动,而Oracle使用基于cpu_count的fast_start_parallel_rollback参数来决定未完成事务的秉性程