(前面由于个人事情较多,所以暂停了博客,现在继续)。
由于PG本身就是个庞大的基础软件,ppg_fdw调用了大量的PG内部的接口,因此很难一行行代码来介绍(上周一个QA同事让我给他讲解代码,被善意的拒绝了。主要是觉得这个完全不可行,因为本身代码比较凌乱,使用了PG的hook机制,目前关于这方面资料比较少,如果没有这方面的基础,很难理解)。因此,在这里,将简单的介绍整体的架构,并且通过TPCH的实例来讲解基本的思想。
目前,就分布式数据库或是数据仓库来说,扩展能力比较强的就属shared-noting (SN)的架构了。例如mysql cluster以及比较著名的Teradata。(也有人会说还有O记的某款,这里只能呵呵了,其实他是shared-disk的,具体效果去问某东吧)。SN架构在上个世纪就被普遍看好,大家有兴趣可以用google scholar搜索。其实鼻祖要数Wisconsin 的gamma系统,研究人员借助这个系统研究了一系列的算法,比如最经典的分布式join算法,可以搜搜看。时下出现了一系列的mpp系统,例如AsterData、GreenPlum、HadoopDB等,虽然看起来十分高大上,其实追本溯源,万变不离其宗。
一般来说,这类系统一般有一个分布式的查询引擎,后端有若干个能执行operator的节点,暂且把前者称为DQP(Distributed Query Processor),后者称为 OP (operator Processor),二者之间可以通过约定的协议通信。简单来说如下所示:
DQP接到一条SQL之后,会经历词法语法分析,查询改写之后,在优化器中生成查询计划。查询计划包括两部分,简单来说,一部分是下推到各个OP上的子计划,一部分是需要在某台上执行的全局operator,最典型的就是agg,当然在某些情况下没有全局operator。对于一个并行系统来说,最重要的考评指标就是加速比了,加速比直接反映了系统的扩展能力。根据阿达姆定律,决定系统的加速比最关键的因素是任务的串行部分。串行部分任务越少,并行任务就越多,系统的扩展能力就越强。而对于一个DQP来说,优化能力越强,下推到OP上的operator越多。
DQP在选择最优查询计划时,一般采用两阶段的优化方式:为全局的operators生成最优的查询计划,对于子查询则下推给OP,让各个OP在本地并行的生成局部最优plan。
好了,在说了一堆理论之后,就此打住(主要是理论的东西太多了,有兴趣自己去看吧),回到ppg_fdw的话题上来。下图是ppg_fdw的处理SQL工作流程:
前面已经描述过PG处理查询SQL的流程,这里就不重复了。对比pg处理SQL的流程来说,ppg_fdw主要的变化时,使用ppg_planner生成了分布式查询计划,查询计划的每个叶子节点都是ppg_fdw的scan接口。DQP通过ppg_fdw的接口将子查询以SQL的形式发给后台的OP(实际就是pgsql节点),OP并行运行SQL之后将结果返回给ppg_fdw的scan节点。
具体说来,ppg_planner的工作主要就是尽可能将operator(project, filter, agg)下推给后台的OP。ppg_planner的输入就是PG中的Query这个数据结构,在pull_up了sublink和subquery之后,使用启发式规则生成全局plan,将能下推的操作deparse生成SQL之后,作为ppg_fdw的scan节点的输入参数。整个的全局计划的叶子节点就是ppg_fdw的scan节点,就是一个foreign_scan节点。
在处理scan的过程中,ppg_fdw主要有四个接口负责处理和OP交互:
postgresBeginForeignScan, postgresIterateForeignScan, postgresReScanForeignScan, postgresEndForeignScan,
其中postgresBeginForeignScan 在整个查询计划初始化被调用,主要负责一些初始化工作,例如申请局部的memcontex、从元信息中解析需要连接的后台的PGSQL的用户名密码等;postgresIterateForeignScan负责从后台的PGSQL获取一条记录,如果没有返回空;postgresReScanForeignScan暂时在ppg_fdw中没有实现,主要是做sublan时使用,To Be Done;postgresEndForeignScan在结束对后台的pgsql时被调用,主要是清理和节点相关的资源等。