Ahsan Hadi曾在EDB工作了15年并担任产品开发高级总监,目前就职于(加拿大),领导多个Geo的开发团队,主要职责是基于国际社区的Postgres开发以及HighHigh Postgres服务器的开发。
前言
我最近有机会参加了一些有关PostgreSQL分片的有趣演讲。第一次演讲是Bruce Momjain在PostgreSQL conf Ottawa(2019年5月)中发表的,他介绍了PostgreSQL中分片的未来,并谈到了PostgreSQL中内置分片的现状和未来。我本人在北京(PGconf.中国-2019年7月)的PostgreSQL大会上发表了第二个演讲,其中我还谈到了PG中内置分片的现状和未来。我本质上使用了Bruce演示文稿中的幻灯片(在得到他的许可的情况下),并添加了几张幻灯片,这些幻灯片讨论了保证可伸缩性的要求,还谈到了全局事务管理器的其他替代方案。
在渥太华举行的PostgreSQL会议期间,我继续遵循在会议周安排早餐会来讨论分片主题的传统。今年的早餐会议有来自EDB,NTT,HighGO和Pivotal,今年的参加人数超过了去年,我们不得不选择更大的早餐桌。此会议的参与者都是准备在这个主题有贡献的公司中来的人,议程是讨论内置分片的当前状态,正在进行的工作,到底发生了什么。本次会议讨论的其他主题是以分片功能为总体目标,并确定可以从分片功能中获益的大致时间范围。
本篇文章将分为3个部分:
第一部分将讨论可伸缩性,什么是不同类型的可伸缩性以及PostgreSQL中当前可用的可伸缩性选项。
第二部分将介绍PG 10中添加的声明式分区以及PostgreSQL中的分片如何工作。
第三部分将介绍内置分片架构,PG中已经存在的下推功能、最后的其余功能以及相关的挑战。
1
Part One
一、什么是可伸缩性,为什么我们需要它
在进行分片之前,重要的是要了解“什么是可伸缩性”,对可伸缩性的需求以及使数据库可伸缩的不同方法。数据库扩展基本上是通过增加诸如I O,内存,CPU或向群集添加其他计算机之类的资源来提高数据库吞吐量及性能。依次增加更多计算也意味着通过使用多个系统的资源来完成这项工作,从而提高数据库性能。仅当您在单个标准服务器上运行PG数据库并且无法满足您的工作负载需求时,数据库才需要可伸缩性。指出需要可伸缩性的原因是数据库性能不佳,数据越来越多且无法容纳在服务器的存储中。
当用户开始看到上一段中提到的一些问题时,他们需要在最终开始考虑可伸缩性之前考虑各种解决方案,应考虑的各种选项如下:
1. 模式优化
需要确保完全优化了模式以处理工作负载和发送到数据库服务器的查询类型。重点是检查用户表是否足够大以进行分区,以便查询可以路由到较小的表,还需要考虑表是否正确范式化。
2. 应用程序逻辑调整
遇到问题时,我们需要考虑的另一个重要领域是应用程序逻辑调整。用户可以考虑是否可以对应用程序进行调整以从数据库中获得最佳性能。这可能涉及重写一些数据库查询,或者在某些情况下,可以在应用程序级别进行一些重构以提高性能。
3. 数据库配置参数调整
这可能是选项中最重要的建议,需要根据工作负载和为数据库生成的流量类型来调整数据库配置参数。重要参数(例如共享内存,工作内存,检查点等)的值需要根据工作环境进行正确配置。这是讨论PG数据库调整的一些重要配置参数的好资料:
https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server
二、可伸缩性选项
在尝试了以上所有选项或更多选项之后,问题仍然存在,那么最好开始考虑可伸缩性选项。可以通过两种方式扩展数据库:

可伸缩性的两个选项很容易理解,垂直可伸缩性是向上扩展,水平可伸缩性是横向扩展。垂直可伸缩性是通过增强单个服务器,向服务器添加更多资源以实现可伸缩性来实现的。水平可伸缩性就是要让更多的计算机投入使用,将更多的节点添加到群集中,以便使用来自多台计算机的资源来获得数据库可伸缩性。
垂直可伸缩性是满足用户需求的一种方式,并且对用户而言也具有成本效益。垂直是首选选项的原因是由于易于配置以及最小的维护和管理开销。您可以通过更快的存储,更多的内存并增加更多和更快的CPU来增强服务器性能,从而提高数据库的性能。主要有两个原因导致垂直可伸缩性不再适合您的工作负载。第一个也是最重要的原因是,增强服务器的成本可能非常高,并且在某些时候,它可能对用户而言成本效益不高。第二个原因是工作负载要求(即存储,并发客户端数和传入流量)变得如此之大,以至于单个服务器无法扩展以适应工作负载。讨论中说PostgreSQL只能扩展到64个内核,然后这条线变成线性的。但我相信基于内核的扩展与并发客户端数和数据库流量有关。
水平可伸缩性是将一台以上的计算机添加到数据库群集中,以便使用其他计算机的资源来获得水平可伸缩性。水平可伸缩性不是首选,因为它涉及配置和维护计算机群集的开销,因此用户需要考虑群集中所有计算机的停机时间,故障转移,备份等。如果由于上一段中给出的原因而无法用单个服务器满足工作负载要求,则水平可伸缩性将成为显而易见的选择。即使没有分片,也可以通过多种方式获得水平可伸缩性,非分片水平可伸缩性最流行的解决方案是Pgpool II的读取可伸缩性。
Pgpool II是介于客户端和PostgreSQL集群之间的中间件产品,并提供连接池,负载平衡,故障转移等功能。Pgpool II通过负载平衡,将写入发送到主节点以及在整个服务器之间平衡读取语句来提供水平可伸缩性。Pgpool II提供连接级别和语句级别的负载平衡,具有连接级别的负载平衡Pgpool II进程与备用节点建立连接,对连接的所有读取均发送到备用节点,而写入则发送到主节点节点。使用语句级连接(Pgpool II 4.1中提供),每个语句在备用节点上实现负载均衡。
关于这个话题本公众号之前发过一篇很好的文章《Pgpool II 4.1以它的号角征服公牛》,可在历史消息里查找。Pgpool II通过负载平衡,将写入发送到主节点以及在备用节点之间平衡读取语句来提供水平可伸缩性。Pgpool II提供连接级别和语句级别的负载平衡,具有连接级别的负载平衡Pgpool II进程与备用节点建立连接,该连接的所有读取均发送到备用节点,而写入则发送到主节点。使用语句级连接(Pgpool II 4.1中提供),每个语句在备用节点上实现负载均衡。
2
Part Two
一、声明式分区
到目前为止,我们已经讨论了可伸缩性,什么是可伸缩性,为什么以及何时需要,以及不同类型的可伸缩性。现在,我们开始深入探讨这一主题,并将讨论PostgreSQL中的声明性分区和分片。
分片功能位于PostgreSQL中的声明性分区功能之上。
声明性分区在PostgreSQL 10中发布,在声明性分区之前PostgreSQL使用表继承和plpgsql触发器提供表分区。
以下示例显示了如何使用PG 10中引入的声明性分区语法对表进行分区:

声明性分区基本上为PostgresSQL中的分区提供了本机支持,使用上面示例中使用的语法,用户可以创建分区表。这会将一个表划分为多个部分,称为分区,这些部分称为分区表。插入分区表中的所有行都将基于分区键被路由到该分区之一。
PostgreSQL 11中增加了声明性分区的许多性能改进,从而改进了分区修剪的代码,分区修剪是基于WHERE谓词提供的质量从搜索中删除某些分区的能力。
二、PostgreSQL中的分片
分片是一种在一个或多个外部服务器上对表进行分区的能力,如表上方所示,通过声明式分区可以将其分为位于同一数据库服务器上的多个分区表。分片允许对表进行分区,以使分区位于外部外部服务器上,而父表位于用户正在创建分片表的主节点上。分片表中使用的所有外部服务器都是PostgreSQL外部服务器,不支持其他外部服务器,例如MongoDB,MySQL等。
下面的示例显示了现在如何在PostgreSQL中创建分片表,然后我们将讨论社区为了添加分片而遵循的方法/体系结构。我们将讨论内置分片已经完成的工作以及剩下的重要部分,并重点介绍了面对的挑战。
1.这是在主服务器上创建的主父表。

2.在远程服务器上,您只需创建一个分区表。这与在主服务器中创建的父分区表相对应,如下所示。

在此示例中,外部服务器为shard1。
3.在主服务器上,需要执行上述步骤,以适当的权限创建postgres_fdw扩展,在将要创建分区表的外部服务器上进行创建,并且需要执行用户映射。

在主服务器上,如上所示创建了分区表,普通分区表与该分区表的区别在于我们指定了外部服务器。在这种情况下,外部服务器是shard_1,我们在其中创建了分区表。
使用上面的示例,用户可以创建分片表,其中分区位于外部服务器上。
请注意,需要在外部服务器上手动创建分区。设置完成后,所有查询都将使用分区修剪逻辑路由到特定的分区。
3
Part Three
一、内置分片架构
PostgreSQL中的内置分片功能使用基于FDW的方法,而FDW基于sql med规范,该规范定义了如何从PostgreSQL服务器访问外部数据源。PostgreSQL提供了许多用于访问外部数据源的外部数据包装器(FDW),postgres_fdw用于访问在外部服务器上运行的Postgres数据库,MySQL_fdw用于从PG访问MySQL数据库,MongoDB_fdw用于访问MongoDB等。
下图说明了PostgreSQL中内置分片的当前方法,这些分区是在外部服务器上创建的,而PostgreSQL FDW用于访问外部服务器,并且计划器使用分区修剪逻辑来确定要访问的分区以及要排除的分区。

二、下推功能
在这种情况下,下推功能是将部分外部查询推送到外部服务器的功能,以减少从外部服务器传输到父节点的数据量。从一开始就属于postgres fdw的两种基本下推技术是select target-list 和WHERE子句下推。

在上面的查询中,计划器将基于分区键(logdate)决定访问哪个分区。WHERE子句将被下推到包含相应分区的外部服务器。这是postgres_fdw中可用的基本下推功能。
分片功能需要更高级的下推功能,以便将最大的操作下推到包含分区的外部服务器,并最大程度地减少通过网线发送到父节点的数据。

上面是在最近的几个主要发行版中已添加到PostgreSQL的一组不错的下推功能。这些功能的好处是,即使没有完整的分片功能,它也已经使许多用例受益。
在我们可以说PostgreSQL中具有Sharding之前,仍然有许多重要的功能。在本节中,我们将讨论这些功能及所面临的挑战。我确信还有其他与数据库集群管理相关的功能,例如备份/故障转移或监视不在此列表中。
1. 2PC用于外部数据包装器事务
当前,FDW事务不支持两阶段提交,这意味着,如果您在一个事务中使用多个外部服务器,并且如果一部分事务在一台外部服务器中失败,则所有外部事务上的整个事务都将失败。为了保证整个数据库群集中的数据一致性,此功能是必需的。
为了支持OLTP工作负载,此功能是必需的,因此对于分片功能非常重要。
此功能的设计建议和补丁已经发送给hackers 好几年了,但是社区对此没有足够的兴趣,该功能的设计仍然非常出色。
2.并行异物扫描
当一个查询在单个查询中查询多个外部扫描时,所有外部扫描都以一种连续的方式依次执行。并行外部扫描功能正在并行执行多个外部扫描。此功能对于OLAP测试用例确实非常重要,例如,如果您正在对被划分为大量分区的大型分区表上运行AVG查询。AVG操作将顺序发送到每个外部服务器,每个外部服务器的结果将发送到父节点,该父节点将在父节点上聚合并发送回客户端。一旦有了并行foriegn扫描功能,所有外部服务器上的所有平均操作都将并行执行,并将结果发送到父节点。
这是完成分片功能所需的关键部分,我们目前具有聚合下推功能,可以将聚合向下发送到外部服务器,但是我们没有在所有分区上并行运行聚合操作的功能。
对于OLAP用例而言,此功能特别重要,拥有大量外部服务器(包含用于大型分区表的分区)以及在并行运行于所有外部服务器分区上的聚合操作的想法非常大。
并行外部扫描功能的基础结构是异步查询执行,这是PostgreSQL中的一个重大更改。在此方面已经完成了一些工作,但是感觉它仍需一个或两个发行版。一旦异步查询执行完成,添加并行外部扫描功能将变得更加容易。
3. 分片管理
当前不会自动在外部服务器上创建分区,需要在外部服务器上手动创建分区。如果要创建具有大量分区和子分区的分区表,这可能是非常繁琐的任务。
假定分片管理功能提供了在外部服务器上自动创建分区和子分区的功能。这将使分片表的创建非常容易。
不打算讨论如何实现此功能的任何设计细节,其基本思想是,分片表语法将建立在声明性分片语法的基础上。postgres_fdw将用于将DDL下推到外部服务器,而FDW仅用于执行SELECT或DML,而在外部源上执行DDL则不是sql med规范的一部分。我们不打算在此博客中讨论此功能的设计。
4. 全局事务管理器/快照管理器
这是分片功能所必需的另一个非常重要且困难的功能。假定全局事务/快照管理器的目的是提供全局事务一致性。本章第1节“用于外部数据包装器事务的2PC”中描述的问题也与全局事务管理器有关。
假设您有两个正在使用分片表的并发客户端,客户端#1试图访问服务器1上的分区,客户端#2也试图访问服务器1上的分区。客户端2分区的一致视图,即客户端2不应在客户端1事务中看到对该分区所做的任何更改(即更新等)。客户端2提交事务后,所有新事务都将看到更新。全局事务管理器假设要确保所有全局事务都能获得数据库集群的一致视图。使用数据库群集的所有并发客户端(具有在多个外部服务器上分表的表)应该看到数据库群集的一致视图。
这是很难解决的问题,像Postgres Professional这样的公司已经尝试通过使用外部事务管理器来解决此问题。到目前为止,社区似乎还没有接受任何解决方案。目前,没有明显的集中精力尝试在核心或在外部组件中实现全局事务管理器。
提到了使用其他方法,例如Clock-SI(分区表的快照隔离)方法,或借鉴其他成功的项目(例如 谷歌cloud spanner和YugaByte)来解决同一问题。
四、结论
对于PostgreSQL来说,具有分片的水平可伸缩性是必不可少的。今天可能只有一部分工作负载需要分片,但是我确信每个人都想知道PostgreSQL可以解决这个问题。还需要注意的是,分片并不是针对所有大数据或高并发工作负载的解决方案,您需要依据两点做出选择:1)可以在逻辑上跨分区对较大表进行分区均衡工作负载;2)查询将受益于使用分片的下推功能和其他功能簇。
正如我在初始部分中提到的那样,完整的分片功能的第一个目标是能够加速长时间运行的复杂查询。这将是OLAP查询,并不是说分片将使OLTP工作负载受益。数据将跨多个服务器而不是单个服务器进行分区。
分片团队应尽快开始进行的另一项重要工作是使用PostgreSQL已包含的功能进行基准测试。我知道没有并行外部扫描,就不可能加快使用多个分区的真正OLAP查询的速度。但是,基准测试的过程应该很快,我们需要确定从分片中受益的工作负载的类型,没有分片的性能是什么以及分片集群的预期性能。我认为随着向集群添加更多分片,我们不能期望性能是线性的。
我在这里要提到的另一个重要点是,有人批评使用FDW机制来实现内置分片。为了有效地处理跨节点通信等,有人建议将其降低到一个较低的水平。一位资深社区成员给出的答案是一个很好的答案,我们正在使用FDW机制来实现此功能,因为这是最快且错误较少的途径。FDW功能已经过尝试和测试,如果我们尝试使用更复杂的方法来实现,则在创建可称为分片的内容之前,将需要分配大量资源和时间;将需要更多的公司投资参与构建。在PostgreSQL发展的角度而言,分片功能很值得更多的公司参与实施共建。






