Postgres Professional 路线图
Postgres Professional(Postgres Pro)是一家由 3 位 PostgreSQL 主要贡献者 Oleg Bartunov、Alexander Korotkov 和 Teodor Sigaev 于 2015 年创立的公司。Postgres Pro 开发了其专有的分支 Postgres Pro Enterprise,并为 PostgreSQL 做出了贡献。我们分享我们的开发路线图,希望更好地协调资源,避免工作重复和利益冲突。
带分片的 Multimaster 集群
带分片的 Multimaster 集群提供读写可扩展性和高可用性,显然是大多数人想要的 DBMS 功能。PostgreSQL 社区开发经验表明,如此庞大的功能应该逐步实现。专业公司在这方面加入了社区的努力。
- 分布式数据库的基本属性是支持分布式事务,包括原子提交和原子可见性。原子提交可以使用 2PC、3PC 或其他分布式提交协议实现,而原子可见性则需要更复杂的技术。为了解决这两个问题,我们提出了可扩展事务管理器 API(xTM)。它是一个可插拔的 API,有两个实现:DTM(快照共享)和 tsDTM(Clock-SI [1])。xTM 在 PGConf.EU 2015 上展示,补丁已发布在 pgsql-hackers [2] 上。xTM 还没有得到足够的审查,无法了解如何将其推入 PostgreSQL 核心。我们正在寻找机会吸引更多社区成员来审查这个问题。
- 我们已经开发了基于 tsDTM 和逻辑解码的高可用性多主集群。将在 PGConf.EU 2016 上展示 [3]。开源版本可在 [4] 获取,商业支持版本作为 Postgres Pro Enterprise 的一部分提供。我们希望最终将多主集群推入 PostgreSQL 核心。但是,根据逐步方法,我们应该首先处理 xTM。
- 我们正在基于我们的多主集群和 FDW 开发分片解决方案。我们将在 2017 年秋季提供测试版。
向量化执行
向量化执行是一种广泛使用的技术,它可以使许多 OLAP 查询加速很多倍。通常,释放向量化执行的全部威力也需要向量化存储(即列存储)。来自 Postgres Professional 的 Konstantin Knizhnik 开发了 vops 扩展,它实现了向量化存储(称为“parquet”布局)和向量化执行。Vops 使用 FDW 接口实现,功能有限,但对于许多实际应用已经足够有用。
我们希望有一天 vops 提供的功能能够成为 PostgreSQL 核心的部分。这就是为什么我们参与了与可插拔存储和执行器改进(包括 JIT)相关的 pgsql-hackers 线程。
可插拔存储
我们在 PostgreSQL 可扩展性方面取得了重大进展:FDW、自定义访问方法、通用 WAL。并且我们绝对需要可插拔存储 API。Alexander Korotkov 在 PGCon 2016 上展示了 API 的概念。简而言之,这种方法意味着开发一种特殊类型的 FDW,它具有扩展的功能(在数据库集群中存储文件的本机方式、vacuum、索引定义等)。但是,在这种情况下,编写新的存储引擎特别困难,因为你无法重用现有的索引访问方法。
另一种方法目前正在 pgsql-hackers 邮件列表中开发 [5]。这种方法的想法是将堆的接口方法提取到一个称为存储访问方法 API 的特殊 API 中。这样,用户就可以用一些与现有索引 AM 兼容的其他数据结构替换堆。但是,任何与现有索引 AM 完全兼容的存储访问方法都必须与我们的堆非常相似。因此,为了实现与我们当前堆完全不同的东西,我们也必须扩展索引 AM API 的能力,包括:存储任意有效负载而不是 TID、索引元组的零售删除、有效负载更新。因此,重用现有索引 AM 的能力可能会让我们走很长一步。
在 PGConf.EU 2017 期间,我们将展示基于 FDW 接口的 内存中 OLTP 存储。本次演讲的主要目标是展示 PostgreSQL 可能拥有的完全不同的数据存储方式。我们希望最终存储访问方法 API 会涵盖我们的内存中引擎。
有效的分区
我们确定了有效分区的以下重要特征
- HASH、RANGE、LIST 分区类型支持
- 快速分区选择 - 在不进行线性时间扫描的情况下选择所需的分区(参见 博客文章)。
- 过滤条件简化 - 简化过滤条件,这些条件涉及到特定分区(参见 博客文章)。
- 哈希连接下推 - 在可能的情况下,自动从分区表的连接切换到单个分区的连接。
- 执行时分区选择 - 在某些情况下,在查询计划期间无法选择所需的分区,但在参数值已知时可以在执行时选择。这些情况是:嵌套循环、从子查询获取的参数、准备语句的通用计划。
- 子分区
我们正在 pg_pathman 扩展中开发这些功能,并牢记 Amit Langote 的声明式分区语法项目。
自适应查询计划
查询计划管理
关键业务应用程序需要对查询计划进行持续监控和调整。由于查询执行的实际资源消耗可能会随着查询计划成本的细微变化而发生显著变化,因此问题变得更加严重。因此,在收集新统计数据后,存在选择错误计划和整体系统性能下降的永久风险。DBA 希望通过冻结对他们特定工作负载至关重要的查询的查询计划来保护自己免受这种风险。Postgres Professional 提供了 sr_plan 扩展,它允许 DBA 冻结所选查询的计划。目前它使用查询计划的完全序列化/反序列化。这种方法的负面之处在于可移植性问题和对模式更改的敏感性。我们正在寻找一种基于平台无关的“逻辑”执行计划序列化方法的更好的解决方案,这种方法对模式更改的敏感性也较低。
机器学习
查询计划程序根据计划的估计成本选择“最便宜”的查询计划。但该成本是使用许多粗略的假设计算的。这就是为什么估计成本可能与实际执行成本大不相同。一种可能性是通过添加诸如多元统计之类的功能来改进成本估计机制本身。另一种可能性是使用查询执行反馈:查看估计参数值与实际参数值有何不同。我们可以应用机器学习技术,利用这种反馈来改进成本估计,以便 DBMS 可以从自身的错误中学习。我们已经 在简单情况下做到了这一点,并计划在以下方向上进行进一步工作
- 扩展已实现的模型以涵盖更多用例,
- 提供必要的基础设施,使我们的机器学习成为一个扩展。
执行时计划
目前,查询计划严格先于查询执行。有时这似乎是一个严重的限制。当计划的一部分已经执行时,有可能根据收集的统计信息显著改进计划的其余部分。我们可以看到两种可以应用这种方法的情况
- 过滤表达式的在线重新排序。在大型表的顺序扫描期间,首先进行最便宜和最具选择性的检查非常重要。但是,估计的选择性和过滤成本不准确,因此基于估计应用过滤器的顺序可能不是最佳的。但是,可以根据其先前执行的统计信息在线重新排序过滤表达式。
- 一些查询可以分成一系列步骤,其中后续步骤可以根据先前步骤的结果重新计划。例如,假设步骤 1 是扫描表 A,步骤 2 是连接表 A 和 B。我们可以使用步骤 1 中的实际行数和数据分布来选择步骤 2 的连接算法。
备份
Postgres Professional 提供了自己的备份解决方案,称为 pg_probackup。它最初是从 pg_arman 分叉出来的,但自从分叉以来它被彻底重写,现在可以被认为是一个独立的产品。有一个 pg_probackup 的开源版本。作为 Postgres Pro Enterprise 的一部分,也提供了商业支持版本的 pg_probackup。
块级增量备份
块级增量备份似乎是完整备份和持续 WAL 归档的不错替代方案,前提是满足以下两个限制。
- 更改的块数量与总块数量相比很低。
- WAL 归档量远大于更改的块量。特别是,这意味着相同的块被更改了。
主要问题是如何获取自上次备份以来更改的块映射。为此,有多种选择。
- 对要备份的数据库集群的所有块进行完整扫描。
- 从 WAL 中提取更改的块。
- 让 PostgreSQL 维护更改的块映射(每页位或 LSN)。
Barman 实现 #1,但在备份期间数据库集群上的高 IO 负载存在问题。我们在 pg_probackup 中实现了 #2 和 #3。#3 需要对核心进行修补。此修补程序已集成到 Postgres Pro Enterprise 中。关于将此功能添加到 PostgreSQL 核心有一些讨论 [6],[7]。
备份验证
许多用户抱怨,一旦进行了文件级备份,检查备份是否有效就很困难。在备份的数据库集群中,我们可以检查很多东西。但是,这些检查应该足够快,以便适合在每次备份后运行,但仍然可以防止常见的错误。pg_probackup 备份验证:它验证每个数据文件是否已备份以及校验和是否有效。
部分备份和部分还原
文件级备份的当前限制是只能备份整个数据库集群。但是,用户希望仅备份集群中的一些数据库,以及仅将一些数据库还原到现有集群中。第二个功能将需要一些侵入性的操作,例如将要还原的数据库堆中的所有 xid 重新写入。但这仍然比 pg_dump/pg_restore 便宜得多。
连接池
长期以来,连接池被认为是一种纯粹的外部解决方案(如 pgbouncer 或 pgpool)。但是,外部池化器存在限制。例如,在会话/事务模式下,池化器的优势是有限的:每个活动会话/事务仍然需要一个单独的后端。在语句模式下,需要额外的努力来维护所有连接处于相同状态。否则,您可能会遇到您正在使用的准备好的语句未定义或 GUC 有错误值的情况。连接池问题的真正解决方案是内置的池化功能,它保证所有用户会话始终以与它们在单独的后端中工作相同的方式工作。但是,这需要对基础架构进行重大重构,例如将整个会话状态存储在共享内存中。
页级数据压缩
我们目前使用 PGLZ 压缩单个值。这很好,但有时只有在将多个值一起压缩时才能实现显着的压缩。这就是我们正在考虑页级数据压缩的原因。允许正常操作压缩页面的重要属性是
- 解压缩单个页面项不应该需要解压缩整个页面;
- 删除页面项不应该增加压缩页面大小;
- 更新项目标头不应该增加压缩页面大小。
为了满足这些要求,我们建议独立压缩页面的每个项目,但使用通用字典,使项目标头不压缩。
等待事件监控
对于 DBA 来说,了解 DBMS 在做什么至关重要。等待事件监控使 DBA 能够快速识别生产系统中的瓶颈。等待事件监控的基本基础架构已提交到 9.6。此基础架构允许读取特定进程的当前等待事件。社区不断努力增加可用等待事件的数量。
可以通过采样累积各个时间点的等待事件统计信息。Postgres Professional 提供 pg_wait_sampling 用于收集此类统计信息。
但是,当采样频率较低时,统计信息可能看起来不够准确。当采样频率很高时,统计信息收集可能会导致高开销。这就是为什么采样统计信息的替代方法是测量各个等待事件的时间。
另一个问题是公开等待事件的参数。例如,使用现有的等待事件监控基础设施,可以观察到某些缓冲区存在高争用。确定此缓冲区属于哪个关系将需要查看等待事件参数:关系的 oid。但是,这将需要等待事件报告的同步协议,或者容忍有时不一致的等待事件信息。
这两个功能都已由 Postgres Professional 实现。在 pgsql-hackers 中的讨论中,这些功能因导致高开销而受到批评。实际开销取决于硬件、操作系统和工作负载。例如,全球排名前 20 位(按流量排名)的互联网公司 Yandex 在生产环境中运行了这两个功能,并且负载很重,没有注意到任何开销。
现在,重要的是要了解 PostgreSQL 社区是否需要这些功能中的任何一个,或者是否拒绝这两个功能。
更好的临时表
系统目录之外的临时表
一些应用程序以非常高的速度创建临时表。在这种情况下,将所有这些临时表写入系统目录会成为一个问题,因为
- 系统目录会因膨胀而受苦;
- OID 以高速度消耗,存在环绕风险。
我们提出了一个修补程序,通过在内存中存储临时表元数据来解决这个问题,而无需触碰持久数据结构 [8]。讨论表明这种方法存在限制。例如,它不支持跟踪来自临时表到在其他会话中创建的对象的依赖关系。但是,我们希望此功能能够进入 PostgreSQL 核心,即使它只支持有限的案例。
备用上的临时表
备用上的临时表是一个非常经常被要求的功能。假设我们可以将临时表元数据存储在内存中,我们可以在备用上实现单事务临时表。为了实现多事务临时表,我们需要解决 MVCC 问题。目前,备用从主服务器接收有关 xid 的信息,并且无法自行发出任何 xid。解决方案可能是引入备用上的“本地 xid”的并行轴。
SQL/JSON 标准
PostgreSQL 是第一个提供有效二进制 JSON 数据类型的关系型 DBMS。但是,时间流逝,SQL/JSON 成为 SQL 标准的一部分。我们提交了为 PostgreSQL 实现 SQL/JSON 的补丁 [9]。我们希望 SQL/JSON 的初始实现能够提交到 PostgreSQL 11,同时还有许多改进需要在后续版本中进行。
通用 WAL
通用 WAL 在 PostgreSQL 9.6 中引入,它允许扩展(如可插拔索引访问方法)写入 WAL 记录,这些记录包含页面之间逐字节的差异。但是,当页面的修改涉及数据偏移(例如,插入或删除索引元组导致页面尾部的偏移)时,通用 WAL 的大小会变得不合理地高。Oleg Ivanov 提出了对通用 WAL 的改进,包括对齐算法的变体。此改进使一般 WAL 记录中数据偏移的紧凑表示成为可能。补丁 已发布在 pgsql-hackes 中并在 commitfest 中注册。
索引
覆盖索引
仅索引扫描是一个很棒的功能,用户可能希望在索引中添加更多列以使仅索引扫描用于更多查询。但是,当您需要强制对某些列子集进行唯一性时,您最终可能会得到两个索引(而不是一个)。
CREATE UNIQUE INDEX olduniqueidx ON oldt USING btree (c1, c2); CREATE INDEX oldcoveringidx ON oldt USING btree (c1, c2, c3, c4);
覆盖索引解决了这个问题。您可以在 INCLUDING 子句中添加列,这些列仅在仅索引扫描期间从索引中获取它们时才需要。
CREATE UNIQUE INDEX newidx ON newt USING btree (c1, c2) INCLUDING (c3, c4);
补丁 已发布在 pgsql-hackes 中并在 commitfest 中注册。
投影索引的 HOT 支持
PostgreSQL 9.4 中引入的 Jsonb 数据类型一直受到欢迎。但是,HOT 是许多类型工作负载的关键优化,它没有充分利用 jsonb。问题是,HOT 仅在没有修改任何索引列时才有效。但是,jsonb 上的表达式索引经常使用。当 jsonb 文档发生更改时,索引表达式可能不受影响,但 HOT 会自动不应用。提议的补丁增加了重新检查特定列的更新是否确实修改了任何索引表达式的能力。然后可以将 HOT 技术有效地应用于 jsonb。补丁 已发布在 pgsql-hackes 中并在 commitfest 中注册。
其他
增量排序
当您的数据集已按该列列表的前缀排序时,增量排序提供更便宜的按列列表排序。例如,您有一个对 c1 的索引,您需要按 c1、c2 对数据集进行排序。然后增量排序可以帮助您,因为它不会排序整个数据集,而是按 c1 值相同的各个组进行排序。当您有 LIMIT 子句时,增量排序非常有用。
补丁 已发布在 pgsql-hackes 中并在 commitfest 中注册。
pl/perl 和 pl/python 的 Jsonb 转换
Jsonb 转换允许您在存储过程中将相应的值视为本机数据结构,而不是字符串。在进一步的增强过程中,我们将以惰性方式将 jsonb 反序列化为本机表示形式(即,从 jsonb 获取单个值不需要完全反序列化)。
补丁 [10],[11] 已发布在 pgsql-hackes 中并在 commitfest 中注册。
64 位 xid
尽管冻结映射非常有用,但具有高事务吞吐量的用户仍然会遇到环绕问题。真正的 64 位 xid 是解决此问题的根本方法。我们正在提议实现 64 位 xid 的路径,而不会以以下方式显着膨胀堆,在堆中使用 64 位值。xmin 和 xmax 保持为 32 位值,而 64 位基数存储在页面标头中。在 32 位值溢出时,会立即执行单页冻结。
补丁 已发布在 pgsql-hackes 中并在 commitfest 中注册。