集群功能

来自 PostgreSQL 维基
跳转到导航跳转到搜索

功能概述

在讨论此功能列表的最初会议上,针对一些与构建 PostgreSQL 集群相关的主要功能进行了“对我来说什么重要”的投票,以帮助确定考虑它们的顺序。结果

为了进一步微调哪些工作对哪些项目有用以及何时可能完成,需要针对这些功能中的每一个回答以下几种问题

  1. 从用户角度来看,此功能有助于添加什么功能?
  2. 哪些复制项目预计会因添加此功能而有所改进?
  3. 实施起来有什么困难?
  4. 列表中是否有其他项目取决于此,或者预计会与之产生重大的积极/消极交互?
  5. 哪些复制项目已经包含类似的功能,或者包含类似功能的原型,这些功能可以用作概念证明或示例实现?
  6. 谁已经在处理它/计划处理它/需要它用于其相关项目?

核心中的优先级功能

将快照导出到其他会话

对于任何类型的并行查询(无论是在同一个节点上还是跨多个节点),您都需要能够将事务的快照导出到另一个后端 - 从原始事务的任何时间点。这包括完整的 XIP 数组(正在进行的事务列表,在快照创建时),以及确保该事务已写入(但未提交)的数据对目标后端可用。对于单个节点,这只是一个空操作,但对于远程后端需要小心。此外,还需要传输一些访问控制信息,以确保并行查询不是安全漏洞。并行查询的 worker 后端永远不需要写入任何数据,因此也应该将其强制为只读模式。并且原始事务在“收集”所有附加到其快照的 worker 后端之前,不应允许继续执行其他查询。

在单个节点上添加快照导出的初始实现,例如最近“同步快照”工作所启动的实现,是这里一个有用的部分步骤 - 特别是考虑到多核服务器现在变得多么普遍。当在多个节点之间使快照一致时,您还需要注意,使本地任务(如 autovacuum 和 analyze)不要干扰共享事务,以免这些事务变成影响整个集群的全局长时间运行的事务。

备注

  • 有一个来自 Simon/Jan 的示例
  • 需要用于快照克隆机制
    • 最好的方法是将其导出到共享内存
  • 同步快照来自 Joachim Wieland 在 pg_dump 的上下文中处理一些类似的问题(在单个节点上将快照共享到多个后端)

杂项

  • 将快照从私有内存复制到一个小文件中
  • 另一个函数允许您从文件中设置快照
  • 但它会覆盖您当前的内存快照(可能很糟糕!:D)
  • 将当前 XID 设置为其他人的 XID?会有什么副作用?
  • 将事务管理推出去?进入 API?在外部存储事务 ID 的状态

全局死锁信息

在任何多主实现中,当尝试在多个节点上维护一组相同的副本时,可能会发生集群范围的死锁。单主集群不需要此功能。

此项有两个方面:检测/通知死锁以及如何处理死锁解决。当本地事务与要从远程节点应用的事务之间发生死锁时(或者两个远程事务之间发生死锁时),需要检测并解决。为了确保节点之间的一致性,它们必须采取相同的措施来解决死锁,例如中止相同的事务。

一个简单的通知 API 不够。这会导致一个节点收到通知并中止事务 A 以解决死锁,而另一个节点会通知它中止了事务 B。您最终将不得不中止两个(或更多)事务而不是一个事务来解决冲突,并且还要担心每个节点在最后是否具有相同的数据。

一些建议的实现

  • 拦截死锁,关闭现有的死锁解析器,并将死锁的解决留给集群解决方案。这种拦截 API 可以注册一个回调,该回调会替换当前的死锁解析器。在检测到死锁后,回调将获得一个包含死锁循环中事务 ID 的列表。然后由该回调选择一个中止来解决冲突。
  • 一个实验性的 PostgresForest 功能使用全局等待图来检测全局死锁(参见 分布式死锁)。每个节点上的 pg_locks 信息可用于构建全局等待图,然后您决定杀死/取消其中一个涉及的事务。
  • 有人建议使用 lock_timeout GUC 来帮助解决这个问题;参见 12

此功能的主要关注点是全局死锁检测的速度。您始终需要在等待足够长的时间以不杀死事务(只是因为竞争激烈)之间找到折衷方案,但仍然需要及时响应以解决真正的死锁。

备注

  • statement_timeout 是一种缓解全局死锁的方法,但它会引入延迟,并且不一定能解决一致性问题
  • Postgres-R 受此问题的影响,旧版本曾经在这个领域做了一些工作,但其实现中没有完整的解决方案。

解析器的 API / 解析器作为独立模块

基于语句的复制项目和负载均衡中间件通常需要决定语句将在哪里执行。如果语句是只读的 SELECT,则可以将其路由到备用节点。如果 PostgreSQL 将其原始解析器作为 C 库提供,那么它将对此目的很有用,因为复制/中间件不需要重新实现自己的 SQL 解析器来做出这种决定。

此外,某些类型的语句预计如果在另一个节点上执行,其值会发生变化,包括

  • CURRENT_TIMESTAMP
  • 序列
  • OID

使用基于语句的复制时,您需要小心地将这些值替换为反映它们在主服务器上的当前值的常量,然后再将它们复制。在解析器级别有一个公开此数据的钩子,可以更容易地识别这些麻烦的结构并在复制它们之前修复它们。

备注

  • pgpool-II 已经构建了自己的 SQL 解析器,它处理了大多数这些情况
  • 如果它直接拥有计划,它可以做得更好,构建其他项目也会更容易
  • 只是第一步,在进入目录(?)之前

在运行时启动/停止归档

使用 PostgreSQL PITR 的几种复制方法使用 PITR 恢复创建基本备份,最初使用一组初始 WAL 段将自己与当前状态同步,然后使用另一种机制保持同步。对于这些应用程序,始终保持归档开启是一种浪费资源的行为。如果可以轻松地打开和关闭归档,它们会更容易。

此功能还有助于解决备用节点出现故障的情况,并且不再可能将存档传递到该节点。现在,当它充满无法回收的 WAL 段时,这会导致主服务器上的磁盘空间耗尽。

目前,更改服务器上的 archive_mode 需要完全重启服务器。但是,您可以更改 archive_command 并只要求重新加载配置。这允许使用两种变通方法来解决此问题。您可以将命令更改为不同的内容以启用/禁用归档。或者,您可以始终保持相同,只需使您的 archive_command 脚本查找有关是否保存要归档的日志文件或是否应该将其丢弃的外部输入。

备注

函数扫描下推

考虑使用 dblink 协议在远程服务器上执行查询的语句

SELECT * FROM dblink('SELECT i FROM remote_tbl') AS (i int) r WHERE i = 5

由于远程服务器不知道将在这里应用的 WHERE 过滤器,因此它必须将 remote_tbl 中的所有行发送回运行查询的服务器。执行查询的服务器然后必须过滤所有这些行,这效率低下。如果扩展了用于返回集合函数 (SRF) 的协议,就可以将“WHERE i = 5”下推到远程服务器。然后它只需要返回需要的行。

SQL/MED 外部数据包装器将能够处理 WHERE 条件并将其传递给外部服务器。 外部表应支持继承和表分区,以实现横向扩展的集群。 主父表可以分区到多个外部表中,每个外部表连接到不同的外部服务器。 它可以像 PL/Proxy 中的分区远程函数调用一样使用。

DDL 触发器

当前的 PostgreSQL 触发器与特定表相关联。 为了能够复制 DDL 语句(如 CREATE 或 ALTER TABLE),需要创建一种新的触发器类型,它针对该语句而不是针对特定对象进行操作。

DDL 触发器对于许多复制方法都很有用,但对于使用 WAL 传输(其中 DDL 已经复制)的方法除外。 此功能对于日志记录目的也比当前设置“log_statement='ddl'”并解析日志文件的方法更有用,例如在高度监控或安全部署中。

修改触发器进入核心/通用数据队列

两个使用触发器来发现数据修改的复制系统 Slony 和 Londiste,将生成的变更集视为一个队列。 它使用与底层数据相同的提交保证插入到数据库中,然后从队列中取出以提交到辅助系统。 这种方法的主要缺点是其开销,对队列 churn 遗留的所有死数据库行的担忧,以及任何轮询队列固有的延迟。

如果这些数据由核心数据库直接保存,并使用基于事务 ID 的索引来提供排序,则可以使其效率更高。 修改触发器和队列数据库部分的实现可以变成一个“通用数据队列”方案,在两个复制系统(以及类似的系统)之间共享。

此区域在 通用修改触发器和通用数据队列规范 中有更详细的说明。

备注

  • Skytools 有一个通用触发器
    • pgQ 特定触发器,简单名称等。

事务提交顺序

  • 顺序 A / B(b 先获得锁,并阻止 A,将以相反顺序提交)
    • 从 WAL 中读取? -> 需要一个接口(PITR 有相同的问题,可以指定停止的位置,但你不知道要给出哪个事务号 -> 因为我们不知道提交顺序时间是什么) -> 使其可选
    • 将有助于多主实现
    • 提交顺序和提交时间戳
    • 两阶段提交顺序(?)
      • 也许多个?
      • xid -> 给我时间戳(在 WAL 中查找 -> 问题:能够

动态共享内存分配

PostgreSQL 在数据库启动时在一个固定大小的块中分配所有共享内存。 一些集群功能可能会受益于能够动态分配共享内存。 例如,“将快照导出到其他会话”将受益于在共享内存中保存其快照信息的空间,而所需的量很难提前预测。

这里提出了一种可能的实现:从共享内存动态分配块(Markus Wanner)

XID 馈送

PostgreSQL 使用不断增长的事务 ID(XID),可以使用 txid_current() 函数查看。 为了实现同步多主集群,服务器需要接受从外部来源注入的 XID。 这将允许同步集群中的所有 XID。

Postgres-XC 实现了这个概念,使用其全局事务管理器。 为此目的提供更通用的 API 将使其他项目受益,并可能简化该项目。

在这里,称为 GTM(全局事务管理器)的专用进程负责为在独立服务器上运行的每个后端提供唯一且有序的 XID,称为 GXID(全局事务 ID)。 这样,在多个服务器上运行的所有事务都会获得唯一的 XID。 当 SQL 节点(在 XC 中称为协调器)要求数据节点处理语句时,它会将 GTM 给出的 GXID 转发到涉及的每个数据节点。

Vacuum 和 Vacuum Analyze 也需要 GXID 才能一致地运行。

未优先

在线创建主键(在后台)

PostgreSQL 中的主键成为针对表的唯一约束,该约束使用唯一索引进行检查。 服务器允许并发创建唯一索引 - 不锁定表 - 但有几个限制和复杂的实现:“执行对表的两次扫描,此外,它必须等待所有可能使用该索引的现有事务终止”。 而且 REINDEX 根本没有类似的并发构建选项可用。

无法 REINDEX CONCURRENTLY 是一个普遍的问题,解决起来很有帮助。 具体来说,在集群上下文中,你并不一定能看到“所有可能使用索引的现有事务”,这意味着需要一种更好的方法来构建在首次创建集群时创建主键索引。

使用约束排除的节点分区

  • 类似于我们希望对不同节点上的分区所做的操作
  • 钩入以跨数据库共享此逻辑
  • 使函数扫描工作? -> 在一个节点上创建一个分区表,其中分区跨多个机器
    • 不容易做到.. 函数扫描在任何其他操作发生之前完全执行..
    • 需要将 where 子句推入函数扫描,分区模块存在吗? ???
    • 外部数据包装器 - 不同表的速度不同
  • 将 WHERE 子句推入函数扫描 -
  • 跨多个节点进行分区 - 事务并行和写入可扩展性
  • 联合查询的重要组成部分

用户级 DDL 触发器

  • 可概括
检索自 “https://wiki.postgresql.ac.cn/index.php?title=ClusterFeatures&oldid=11410”