内容提要
PostgreSQL 16 引入了查询规划器的改进,以加快 SQL 查询速度,包括增量排序、更好的聚合函数处理、嵌套循环的记忆化、改进的哈希连接优化、并行哈希连接支持、窗口函数优化、分区表上的左连接移除、去重子句优化以及改进的合并连接排序。这些优化显著提高了性能并减少了内存使用。
延伸解读
增量排序的扩展应用
PG16 将增量排序的应用范围扩展到了 SELECT DISTINCT 查询。此前,规划器仅考虑全排序,而增量排序能利用索引提供的前缀有序性,仅对变化部分排序,从而减少内存使用和排序开销。例如,在包含索引的列上执行 DISTINCT 时,PG16 可能选择增量排序,避免 PG15 中哈希聚合可能产生的大量磁盘溢出,显著提升性能。
聚合与窗口函数的优化
PG16 优化了聚合函数和窗口函数的处理。对于包含 ORDER BY 或 DISTINCT 的聚合,规划器现在尝试提供预排序输入,避免执行器内部排序。窗口函数方面,规划器能识别哪些函数不关心 ROWS/RANGE 选项,从而跳过不必要的 peer 行检查,尤其在使用 row_number() 等函数限制结果时,可大幅减少读取行数,提升查询速度。
连接与去重优化
PG16 在连接和去重方面有多项改进。反连接现在支持选择较小的表构建哈希表,减少内存使用并提升性能。并行哈希连接支持 FULL 和 RIGHT 连接类型,允许此类查询并行执行。此外,规划器能识别 DISTINCT 列上的等值条件,将查询优化为 LIMIT 1,避免不必要的去重操作。左连接移除优化也扩展到了分区表。
合并连接与排序策略调整
PG16 调整了合并连接的排序策略,从要求排序顺序完全匹配改为只需至少一个前导列有序,从而允许使用增量排序来满足上层操作(如 GROUP BY、ORDER BY)的需求。增量排序能利用部分有序输入,减少排序工作量和内存消耗,尤其在大结果集排序时,可降低磁盘溢出风险,提升查询效率。
Q&A
PostgreSQL 16 的查询规划器主要引入了哪些改进?
PostgreSQL 16 的查询规划器引入了 10 项改进,包括:增量排序、聚合函数处理优化、嵌套循环的记忆化(Memoize)、哈希连接优化、并行哈希连接支持 FULL 和 RIGHT 连接、窗口函数优化、分区表上的左连接移除、去重子句优化以及合并连接排序改进。这些优化旨在加快 SQL 查询速度并减少内存使用。
PostgreSQL 16 中的增量排序是如何工作的?
增量排序利用结果集已按一个或多个前导列排序的事实,仅对剩余列进行排序。例如,如果有一个 btree 索引在列 a 上,并且需要按 a, b 排序,则可以使用该索引(提供按 a 预排序的结果),并仅在 a 的值变化时对已看到的行进行排序。使用快速排序算法时,排序多个较小的组比排序一个大组更高效。
PostgreSQL 16 对 SELECT DISTINCT 查询的排序优化是什么?
在 PostgreSQL 16 之前,为 SELECT DISTINCT 查询选择排序方法时,规划器只考虑执行完整排序(比增量排序更昂贵)。PostgreSQL 16 的查询规划器现在会考虑对 SELECT DISTINCT 查询执行增量排序,从而减少排序所需的工作量。
PostgreSQL 16 如何优化聚合函数中的 ORDER BY 或 DISTINCT?
在 PostgreSQL 15 及更早版本中,包含 ORDER BY 或 DISTINCT 的聚合函数会导致执行器总是在聚合节点内执行排序。PostgreSQL 16 的查询规划器现在尝试形成以正确顺序向聚合节点提供行的计划,执行器能够识别这一点,并在行已按正确顺序预排序时跳过排序。
PostgreSQL 16 中 Memoize 计划节点有什么新用途?
Memoize 计划节点在 PostgreSQL 14 中引入,作为参数化嵌套循环和其内侧之间的缓存层。PostgreSQL 16 的查询规划器现在考虑在 UNION ALL 查询出现在参数化嵌套循环的内侧时使用 Memoize,从而减少重复执行子节点的开销,提升性能。
PostgreSQL 16 在反连接(Anti Join)中如何选择哈希表?
在 PostgreSQL 16 之前,反连接(如使用 NOT EXISTS 的查询)总是将 NOT EXISTS 部分提到的表放在连接的内侧,导致可能需要在较大的表上构建哈希表。PostgreSQL 16 的查询规划器现在可以选择对两个表中较小的表进行哈希,这是因为 PostgreSQL 16 支持右反连接(Right Anti Join),从而减少内存使用并提高性能。
PostgreSQL 16 对并行哈希连接做了哪些改进?
PostgreSQL 11 引入了并行哈希连接,允许多个并行工作进程协助构建单个哈希表。PostgreSQL 16 改进了并行哈希连接,现在支持 FULL 和 RIGHT 连接类型。这使得包含 FULL OUTER JOIN 的查询可以并行执行,也允许右连接计划并行执行。
PostgreSQL 16 如何优化窗口函数的处理?
当查询包含窗口函数(如 row_number()、rank() 等)且窗口子句未指定 ROWS 选项时,PostgreSQL 会默认使用 RANGE 选项,这会导致执行器查找非对等行,可能代价高昂。PostgreSQL 16 的查询规划器知道哪些窗口函数关心 ROWS/RANGE 选项,并将此信息传递给执行器,从而跳过不必要的对等行检查,减少额外处理。
PostgreSQL 16 在分区表上的左连接移除优化是什么?
PostgreSQL 长期以来能够移除左连接,当查询不需要左连接表的任何列且连接不会重复行时。但在 PostgreSQL 16 之前,分区表不支持左连接移除,因为规划器用于确定内侧行是否可能重复外侧行的证明在分区表上不存在。PostgreSQL 16 的查询规划器现在允许在分区表上进行左连接移除优化,这通常有助于视图查询。
PostgreSQL 16 的去重子句优化是如何工作的?
当查询规划器能够检测到所有行都包含相同值时,它可以避免包含用于去重的计划节点。例如,如果 DISTINCT 子句中的每一列在 WHERE 子句中都有相等条件,则所有输出行在每列中都具有相同的值。PostgreSQL 16 的查询规划器可以利用这一知识,简单地将查询结果限制为 1 行,从而显著提高性能。