被低估的 Postgres:Extended Statistics 实现更佳执行计划
Source: Dev.to
请提供您希望翻译的文章正文内容,我将把它完整地翻译成简体中文,并保留原有的格式、Markdown 语法以及代码块和链接不变。谢谢!
概述

PostgreSQL 的一个鲜为人知且使用率更低的特性(至少在生产环境中我很少见到)是 扩展统计信息,它用于捕获和编码多列值之间的依赖关系、关联以及可能的组合。
查询规划器通常基于单列统计信息来生成执行计划,但列之间的关系在实际应用中非常常见。我个人本来希望能更早了解这个特性。通常,我会再建一个索引 hoping it improves access patterns, but the cost of all the indexes quickly reaches or even exceeds the size of the original table. 扩展统计信息正是为了解决这个问题——它往往是额外索引的更轻量替代方案。
单列统计的缺陷
PostgreSQL 的查询规划器在估算不同执行计划的成本时严重依赖统计信息。默认情况下,它会收集各个列的统计信息。当列之间相互独立时,这种做法效果很好。然而,在许多实际场景中,列之间是相关的。
考虑一个名为 users 的表,包含 city 和 country 两列。如果查询条件是 city = 'Paris' 且 country = 'France',规划器可能会低估返回的行数,因为它假设这两个条件相互独立,将 city = 'Paris' 的概率与 country = 'France' 的概率相乘。实际上,只要城市是巴黎,国家几乎必定是法国。
这种低估会导致规划器选择次优的执行计划(例如选择嵌套循环连接而不是哈希连接),从而导致性能不佳。
输入扩展统计
扩展统计允许您向 PostgreSQL 说明列之间的关系。您可以在多个列上创建统计信息,使规划器能够做出更准确的估计。
创建扩展统计信息
您可以使用 CREATE STATISTICS 命令创建扩展统计信息。以下是一个简单示例:
CREATE STATISTICS city_country_stats (dependencies)
ON city, country
FROM users;
此命令创建的统计信息捕获 users 表中 city 和 country 列之间的函数依赖关系。
扩展统计的类型
- Dependencies – 捕获列之间的函数依赖(例如,
city决定country)。对具有多个相等条件的查询很有用。 - N‑Distinct – 估计一组列组合的不同值数量。对
GROUP BY查询很有用。 - MCV (Most Common Values) – 存储跨多列的最常见值组合。对具有复杂条件的查询(例如,不等式、
OR子句)很有用。
CREATE STATISTICS complex_stats (dependencies, mcv)
ON col1, col2, col3
FROM my_table;
何时使用扩展统计信息
扩展统计信息在以下情况下特别有用:
- 当查询在相关列上有多个条件时。
- 当查询规划器持续低估或高估行数时。
- 当你考虑仅为改进查询规划而添加多列索引时(扩展统计信息通常是更轻量的替代方案)。
常见陷阱
-
过度使用 – 不要在每个列组合上创建扩展统计信息。它们会占用存储并增加
ANALYZE操作的开销。仅在识别出特定查询计划问题时才创建它们。 -
忘记执行 ANALYZE – 创建扩展统计信息后,必须对表运行
ANALYZE,才能让规划器开始使用它们。 -
复杂表达式 – 扩展统计目前仅适用于简单列引用,不支持表达式(例如
LOWER(col))。
结论
扩展统计是 PostgreSQL 中一个强大但未被充分利用的功能。通过向查询规划器提供列之间关系的信息,您可以显著提升复杂查询的性能,而无需额外的索引开销。
- 使用
CREATE STATISTICS捕获依赖关系、n‑distinct 值或 MCV。 - 针对规划器估计不准的特定查询进行优化。
- 在创建统计信息后记得对表执行
ANALYZE。