基于fdw的跨Greenplum集群数据库查询实现

Posted Greenplum中文社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于fdw的跨Greenplum集群数据库查询实现相关的知识,希望对你有一定的参考价值。

《Greenplum走进全国》系列技术研讨会在告别后,于7月3日,携原厂、社区和合作伙伴的讲师们走进山东济南。活动中,我们与当地的Greenplumer进行了深入的交流,并带来了四个精彩主题演讲。为了能让更多社区的小伙伴学习相关内容,我们将陆续把主题演讲整理成文章,欢迎关注!

基于fdw的跨Greenplum集群数据库查询实现

今天和大家分享的主题是《基于fdw的跨Greenplum集群数据库查询实现》。其实这个标题并不100%精确,今天分享的内容并不只局限于跨集群的查询,还适用于跨数据库的查询场景。实际环境中用户常常会有跨Greenplum集群或者跨数据库的查询需求,Greenplum的新版本很快将会支持这项功能。通过这个功能,用户可以无缝透明地跨数据库或者跨集群运行SQL查询。这项功能基于postgres_fdw技术以及GP独有的segment级别并行cursor技术。本话题将介绍该功能相关的技术实现。


这一功能的需求主要来自工业界关于数据库联邦的需求以及Greenplum用户的通用需求。


Postgres跨节点数据库查询


在介绍Greenplum是如何做到跨集群查询之前,我们先来看看Postgres (PG)是如何做到跨节点数据库查询的。众所周知Greenplum是基于PostgreSQL的MPP数据库,做了大量的优化和增强来满足用户的需求。PostgreSQL上跨节点数据库查询主要可以通过两个组件来完成。


  1. foreign data wrapper(fdw)

  2. dblink


这两个组件,fdw相对dblink使用更透明,语法更加标准,性能更好。此外,PG社区一直在对fdw做优化,每个PG版本性能都有所提高或者提供了更多的功能。其中postgres_fdw模块是用来连接postgres与postgres的fdw模块。postgres_fdw代码在Postgres代码仓库中,所以它的功能一直和PG核心代码一样在保持演进。


典型的postgres_fdw使用方法如下,

  • create extension postgres_fdw; -- 装载postgres_fdw模块

  • CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'foo', dbname 'foodb', port '5432');

  • CREATE USER MAPPING FOR bob SERVER myserver OPTIONS (user 'bob', password 'secret');

  • CREATE FOREIGN TABLE foreign_table (id integer NOT NULL, data text)SERVER myserver;

  • 或者 IMPORT FOREIGN SCHEMA 

  • 如何使用postgres_fdw?和正常表一样操作(读甚至写)foreign table。


Postgres社区在持续不断提高fdw的功能和性能。下图是PostgreSQL的不同版本所做的fdw相关的增强。


基于fdw的跨Greenplum集群数据库查询实现

(来源于http://www.interdb.jp/pg/pgsql04.html)


Postgres 13和今年下半年要发布的Postgres 14做了更多fdw相关的功能或性能增强。其中PostgreSQL 13中所做的功能包括Passwordless,ssl认证等,具体可以参考链接:

https://www.percona.com/blog/2020/09/30/postgresql_fdw-authentication-changes-in-postgresql-13/


PostgreSQL 14中所做的功能包括parallel Foreign scan, bulk insert, truncate等,具体可以参考链接

https://www.percona.com/blog/2021/06/02/postgres_fdw-enhancement-in-postgresql-14/


那么在PostgreSQL跨节点数据库查询中,postgres_fdw如何读取呢?


首先Foreign Scan执行器节点代码会

  • 创建一个Cursor来读取远端的表

  • 调用Fetch语句返回上层执行器tuples


Postgres_fdw代码提供各种fdw接口的callback,比如用于Scan操作的:

/* Functions for scanning foreign tables */ routine->GetForeignRelSize = postgresGetForeignRelSize; routine->GetForeignPaths = postgresGetForeignPaths; routine->GetForeignPlan = postgresGetForeignPlan; routine->BeginForeignScan = postgresBeginForeignScan; routine->IterateForeignScan = postgresIterateForeignScan; routine->ReScanForeignScan = postgresReScanForeignScan; routine->EndForeignScan = postgresEndForeignScan;

      

fdw提供丰富的接口函数以满足多种fdw需求。


跨Greenplum集群cluster查询




讲完PostgreSQL跨节点数据库查询,我们现在来看看跨Greenplum集群查询是怎么做的。


Greenplum集群的查询还是借助于postgres_fdw模块。理论上使用QD(coordinator或者master上后端进程)到QD的postgres_fdw其实就可以做了,但是走coordinator通常会很慢。最好的办法是根据查询计划,本地segments通过各自的postgres_fdw读取远端的segments的数据,但是注意不要求N:N的本地和远端segment数量一样,要允许异构。


在设计跨Greenplum集群cluster查询时,

  • 首先我们设计了新的cursor类型语法:Parallel Retrieve Cursor

  • 修改了postgres_fdw,支持和使用这种cursor

  • 本地segments通过各自的cursor连接远端一个segment来做fdw的查询。


那么为什么我们需要parallel retrieve cursor,而不用local的cursor呢?主要原因是可以在coordinator上统一认证、统一管理、全局事务。要注意的一点是:Parallel Retrieve Cursor可以不局限于跨Greenplum查询使用,这个功能有很多的使用想象空间;


接下来,我们来看一个Parallel Retrieve Cursor使用的demo。


首先我们启动一个到Greenplum集群的链接,然后运行下面的SQL语句


# BEGIN;# DECLARE c1 PARALLEL RETRIEVE CURSOR FOR SELECT * FROM t1;# SELECT * FROM gp_endpoints() WHERE cursorname='c1';gp_segment_id | auth_token | cursorname | sessionid | hostname | port | userid | state | endpointname---------------+----------------------------------+------------+-----------+----------+------+--------+-------+-------------- 0 | a612d36ac7bfb6b4a1f147cfd283dea2 | c1 | 3586 | host67 | 7002 | 10 | READY | c100000e02 1 | d39e6b62200a9e19ba5ec21ac1fc8cab | c1 | 3586 | host67 | 7003 | 10 | READY | c100000e02 2 | a4e6fcd468039e77f91e62578deb40b9 | c1 | 3586 | host67 | 7004 | 10 | READY | c100000e02(3 rows)


Endpoint代表这个cursor在某个执行节点的内部单元(根据计划,endpoint可能在某个节点或者多个节点)。


接着我们启动retrieve类型连接到某个segment读取数据。


$ PGOPTIONS='-c gp_retrieve_conn=true' psql -p 7002Password for user pguo: <输入:auth_token>postgres=# RETRIEVE 2 FROM ENDPOINT c100000e02; <- 新的语法获取数据,类似于FETCH。a---23(2 rows)


注意:Parallel Retrieve Cursor只允许RETRIEVE语句来获取数据,RETRIEVE语句也只可以在gp_retrieve_conn=true的连接上使用,这种连接也只允许RETRIEVE语句,暗含了utility模式。


那么我们为什么不在RETRIEVE语句中使用cursor名字呢?这是因为要防止不同sesson的parallel retrieve cursor名字重名冲突。那么我们为什么需要指定endpoint呢?这是因为一个session用户可以declare多个cursor。


在使用Parrallel Retrieve Cursor的过程中要注重安全性的问题,我们没有提及太多细节,但是内部代码做了不少考虑。


我们也提供一些别的相关的UDF函数:


gp_check_parallel_retrieve_cursor(cursorname) - 返回是否所有数据获取完毕gp_wait_parallel_retrieve_cursor(cursorname   -  等候直到所有数据获取完毕gp_segment_endpoints()                        -  某个segment上endpoint信息 


Parallel Retrieve Cursor的技术实现类似于普通Cursor,也是类似Greenplum生成Plan,但是区别是不需要一个最顶层的gather motion节点,gang的管理也类似,QD到QE链接,cursor语句单独的reader gang。


Retrieve链接后端和QE的cursor后端进程通过PG的shm_mq (shared memory queue)来通信数据,shm_mq也用于PG的Parallel Scanning处理。具体来说,是通过shm_mq创建的Dest Receiver,使用Endpoint内部数据结构关联。这里的一些代码逻辑处理需要谨慎。


那我们是如何做到跨Greenplum集群查询呢?


  • 本地GP集群QD节点:Foreign Scan执行器节点预先创建相应的Parallel Retrieve Cursor并记录相应信息到plan中(比如登录token等),这些信息通过gp_endpoints() UDF来获取,实现是调用BeginForeignScan()接口

  • 本地GP集群的QE节点,在远端segment节点分别建立retrieve连接,实现也是调用BeginForeignScan()接口

  • QE节点读取数据(通过RETRIEVE语句批次读取数据)


调用IterateForeignScan()接口返回上层执行器节点tuple。


今天介绍的相关功能计划于Greenplum 6和以上版本进行实现。Parallel Retrieve Cursor部分基本完成,主要是稳定性等余下工作。Fdw方面工作(local cluster)相对简单,已近完成,主要是联调和测试。目前的开发是现在基于master分支代码,会Backport到Greenplum 6,敬请期待。



点击文末“ 阅读原文 ”,获取Greenplum中文资源。



来一波 “在看”、“分享”和 “赞” 吧!


以上是关于基于fdw的跨Greenplum集群数据库查询实现的主要内容,如果未能解决你的问题,请参考以下文章

Greenplum 集群性能测试

Greenplum 集群性能测试

Greenplum数据库中的索引和集群

Greenplum数据库中的索引和集群

基于Promethues与Grafana的Greenplum分布式数据库监控的实现

PostgreSQL通过oracle_fdw访问Oracle数据