PG统计信息与选择率

1401 字
7 分钟
PG统计信息与选择率

概述#

PostgreSQL 数据库的物理优化需要计算各种物理路径的代价,而代价估算的过程严重地依赖于数据库的统计信息,统计信息是否能准确地描述表中的数据分布情况是决定代价的准确性的重要条件之一。 通过统计信息,代价估算系统就可以了解一个表有多少行数据、用了多少个数据页面、某个值出现的频率等,然后根据这些信息计算出一个约束条件能过滤掉多少数据,这种约束条件过滤出的数据占总数据量的比例称为“选择率”。

1. 统计信息#

1.1 PostgreSQL数据库支持多种形式的统计信息,包括单列的统计信息和多列(扩展)的统计信息,单列的统计信息是指对每个表的每一个属性(列)都在PG_STATISTIC系统表中产生一个对应的统计信息元组(行),这个元组负责从多个角度描绘这个属性中的数据分布,单列统计信息主要包含表5-1中的这些形式。

表5-1 单列统计信息类型说明
类型说明
STATISTIC_KIND_MCV高频值(常见值),在一个列里出现最频繁的值,按照出现的 频率进行排序,并且生成一个一一对应的频率数组,这样就能 知道一个列中有哪些高频值,这些高频值的频率是多少
STATISTIC_KIND_HISTOGRAM直方图,PostgreSQL数据库使用等频直方图来描述一个列中的 数据的分布,高频值不会出现在直方图中,这就保证了数据的 分布是相对平坦的
STATISTIC_KIND_CORRELATION相关系数,相关系数记录的是当前列未排序的数据分布和排序 后的数据分布的相关性,这个值通常在索引扫描时用来估计代价,假设一个列未排序和排序之后的相关性是0,也就是完全不 相关,那么索引扫描的代价就会高一些
STATISTIC_KIND_MCELEM类型高频值(常见值),用于数组类型或者一些其他类型, PostgreSQL数据库提供了ts_typanalyze系统函数来负责生成这 种类型的统计信息
STATISTIC_KIND_DECHIST数组类型直方图,用于给数组类型生成直方图,PostgreSQL数 据库提供了array_typanalyze系统函数来负责生成这种类型的统 计信息
STATISTIC_KIND_RANGE_LENGT H_HISTOGRAM数组类型直方图,用于给数组类型生成直方图,PostgreSQL数 据库提供了array_typanalyze系统函数来负责生成这种类型的统 计信息
STATISTIC_KIND_BOUNDS_HISTO GRAM为Range类型生成一个基于边界的直方图,这种类型的直方图也 通过range_typanalyze系统函数来进行统计
表5-2 多列统计信息类型说明
类型说明
STATS_EXT_NDISTINCT和单列统计信息中的stadistinct是类似的,stadistinct中记录的是单 列中去掉NULL值和消重之后的数据量或者比例, STATS_EXT_NDISTINCT类型的统计信息则记录的是基于多列 的消重之后的数据量
STATS_EXT_DEPENDENCIES计算各个列之间的函数依赖度,通过函数依赖度计算各个列之间 的依赖关系,从而得到准确的统计信息
--选择率高,采用顺序扫描的方法获取数据
postgres=# EXPLAIN SELECT * FROM STUDENT WHERE sno > 2;
QUERY PLAN -------------------------------------------------------------
Seq Scan on student (cost=0.00..224.00 rows=9999 width=10) Filter: (sno > 2)
(2 rows)
--选择率低,采用索引扫描的方法获取数据
postgres=# EXPLAIN SELECT * FROM STUDENT WHERE sno < 2;
QUERY PLAN -----------------------------------------------------------------------------
Index Scan using student_pkey on student (cost=0.29..8.30 rows=1 width=10) Index Cond: (sno < 2)
(2 rows)

通过示例可以看出,在选择率高的情况下查询路径选择了顺序扫描(SeqScan)的方式,在选择率低的情况下查询路径选择了索引扫描(IndexScan)。这是因为对于索引扫描而言,它产生了“随机读”,如图5-1所示,PostgreSQL数据库中堆表的数据是无序的,而主键索引则以 B 树的方式进行存储,B 树的叶子节点是有序的,如果通过顺序扫描(SeqScan)的方式对STUDENT表进行遍历,则需要对每一个STUDENT 表中的元组应用约束条件,筛选出符合约束条件的元组作为结果。如果通过索引扫描的方式,则可以借助B树索引的有序性,快速定位索引项的位置,每一个索引项都有一个堆元组的“地址”,这个“地址”指向了该索引项对应的STUDENT表中的元组,因为索引项是有序的,而STUDENT表中的元组是无序的,所以这时就产生了随机读。 而在PostgreSQL 数据库中对于顺序读和随机读定义了不同的代价。

#define DEFAULT_SEQ_PAGE_COST 1.0 //顺序读的单页代价
#define DEFAULT_RANDOM_PAGE_COST 4.0 //随机读的单页代价

如果选择率比较高,那么随机读的代价累计起来就很可观了,因此在选择率高的情况下会 选择顺序扫描,而当选择率比较低时,顺序扫描仍然要把整个表的数据过滤一遍。索引扫描的 单个随机读代价虽然高,但总量远远小于顺序读的数据量,因此顺序读的累计的代价就会超过索引扫描的代价,这时就会选择索引扫描作为执行路径。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

PG统计信息与选择率
https://blog.2228472062.workers.dev/posts/postgresql/postgresql统计信息和选择率/
作者
达令鹿
发布于
2026-06-16
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
达令鹿
Hello, I'm DarlingDeer.
公告
欢迎来到我的博客!
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
3
分类
2
标签
7
总字数
5,250
运行时长
0
最后活动
0 天前
站点信息
构建平台
Cloudflare Workers
博客版本
Firefly v6.12.1
文章许可
CC BY-NC-SA 4.0

文章目录