本节讨论成本优化器的基础:统计。通过示例进行讲解。这里会由很多执行计划,后续会更加详细讨论这些计划如何运行。现在只需要注意每个计划的第一行看到的数字以及行数。这些是行数估计值。

基本统计

pg_class系统表存储着基本关系级别的统计信息。统计信息包括:

1) 关系的行数reltuples

2) 关系大小,以页为单位relpages

3) 关系visibility map中被标记的页的页数relallvisible

SELECT reltuples, relpages, relallvisible
FROM pg_class WHERE relname = 'flights';reltuples | relpages | relallvisible
−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−−−−−214867 |     2624 |         2624
(1 row)

对于没有过滤条件的查询,基数估算值等于reltuples:

EXPLAIN SELECT * FROM flights;QUERY PLAN
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−Seq Scan on flights  (cost=0.00..4772.67 rows=214867 width=63)
(1 row)

自动或手动分析期间采集统计信息。基本统计数据是重要信息,在执行某些操作时也会计算处理,例如VACUUM FULL和CLUSTER或CREATE INDEX和REINDEX。系统还会在VACCUM期间更新统计信息。

为采集统计信息,分析器随机select 300*default_statistics_target行数(默认值是100,因此总共为30000行)。此处未考虑表大小,因为总体数据集大小对足以进行精确统计的样本大小没有影响。

从300*default_statistics_target随机页中选择随机行。如果表比预期的样本大小小,分析器读取整个表。

大表中,统计数据将不准确。因为分析器不会扫描每一行。即便扫描每一行,统计数据也总会有过期,因为表中数据一直在变化。无论如何,我们不需要统计数据那么精确:高达一个数量级的变化仍然足够准确以产生适当的计划。让我们创建一个禁用自动vacuum的表的副本flights,以便我们可以控制何时进行分析。

CREATE TABLE flights_copy(LIKE flights) WITH (autovacuum_enabled = false);

新表中还没有统计信息:

SELECT reltuples, relpages, relallvisible
FROM pg_class WHERE relname = 'flights_copy';reltuples | relpages | relallvisible
−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−−−−−−1 |        0 |             0
(1 row)

reltuples=-1(PG14及更高版本)帮助我们区分从没采集统计信息的表和空表。通常情况下,新创建的表会立即填充,规划器对新表无感知,因此默认情况下假定该表10页:

EXPLAIN SELECT * FROM flights_copy;QUERY PLAN
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−Seq Scan on flights_copy  (cost=0.00..14.10 rows=410 width=170)
(1 row)

规划器基于单行宽度计算行个数。宽度通常是在分析期间计算的平均值。但是,这次没有分析数据,因此系统根据列数据类型来估算宽度。从flights表拷贝数据到新表然后执行分析器:

INSERT INTO flights_copy SELECT * FROM flights;
INSERT 0 214867
ANALYZE flights_copy;

现在统计信息匹配真实行数。该表足够紧凑,分析器可以遍历每一行:

SELECT reltuples, relpages, relallvisible
FROM pg_class WHERE relname = 'flights_copy';reltuples | relpages | relallvisible
−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−−−−−214867 |     2624 |             0
(1 row)

Vacuum后relallvisible值会更新:

VACUUM flights_copy;
SELECT relallvisible FROM pg_class WHERE relname = 'flights_copy';relallvisible
−−−−−−−−−−−−−−−2624
(1 row)

评估index-only扫描代价的时候会用到这个值。

我们保留老的统计信息,插入1倍元组,看下规划器得到的基数是多少:

INSERT INTO flights_copy SELECT * FROM flights;SELECT count(*) FROM flights_copy;count
−−−−−−−−429734
(1 row)EXPLAIN SELECT * FROM flights_copy;QUERY PLAN
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−Seq Scan on flights_copy  (cost=0.00..9545.34 rows=429734 width=63)
(1 row)

尽管pg_class数据已过时,但该估计是准确的:

SELECT reltuples, relpages
FROM pg_class WHERE relname = 'flights_copy';reltuples | relpages
−−−−−−−−−−−+−−−−−−−−−−214867 |     2624
(1 row)

规划器注意到数据文件的大小不再匹配旧的relpages值,因此reltuples适当缩放以提高准确性。文件大小增加了1倍,因此行数也应该相应调整(假设数据密度不变):

SELECT reltuples *(pg_relation_size('flights_copy') / 8192) / relpages
FROM pg_class WHERE relname = 'flights_copy';?column?
−−−−−−−−−−429734
(1 row)

这种调整并不总是有效,例如可以删除几行,但估算值不会变化。但当发生较大变化时,这种方法可以让统计数据保持不变,直到analyze。

NULL值

虽然正统主义者看不起,但是NULL值可以方便地表示未知或者不存在的值。但是特殊值需要特殊处理。使用NULL值时需要考虑一些实际的注意事项。布尔逻辑变成三进制,NOT IN构造开始表现的很奇怪。目前尚不清楚NULL值是否被视为低于或者高于常规值(特殊从句NULLS FIRST和NULLS LAST帮助)。聚合函数中使用NULL值也很粗略。因为NULL值实际上根本不是值,规划器需要额外的数据来容纳他们。

除了基本的关系级别统计信息外,分析器还收集关系中每一列的统计信息。此数据存储在pg_statistic系统表中,可以使用pg_stats视图方便地显示。

NULL值的分数是列级别的统计信息。被指定为pg_stats中的null_frac。本例中,一些飞机还没起飞,所以他们的起飞时间是不确定的:

EXPLAIN SELECT * FROM flights WHERE actual_departure IS NULL;QUERY PLAN
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−Seq Scan on flights  (cost=0.00..4772.67 rows=16036 width=63)Filter: (actual_departure IS NULL)
(2 rows)

优化器将总行数乘以NULL分数:

SELECT round(reltuples * s.null_frac) AS rows
FROM pg_classJOIN pg_stats s ON s.tablename = relname
WHERE s.tablename = 'flights'AND s.attname = 'actual_departure';rows
−−−−−−−16036
(1 row)

这与 16348 的真实值足够接近。

Distinct值

一列中distinct值个数存储在pg_stats的n_distinct字段。如果n_distinct为负值,则其绝对值表示不同值的比例。例如,对于-1值,表示这列的值都是唯一的。当不同值的数量达到行数的10%或更多时,分析器将切换到分数模式。此时当修改数据时该比例通常会保持不变。如果不同值的数量计算不准确(因为样本恰好不具有代表性),您可以手动设置此值:

ALTER TABLE ... ALTER COLUMN ... SET (n_distinct = ...);

在数据均匀分布下,不同值的数量很有用。考虑“column = expression”子句的基数估计。如果在规划阶段表达式值未知,则规划器假定表达式同样可能从列中返回任何值。

EXPLAIN
SELECT * FROM flights WHERE departure_airport = (SELECT airport_code FROM airports WHERE city = 'Saint Petersburg'
);QUERY PLAN
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−Seq Scan on flights  (cost=30.56..5340.40 rows=2066 width=63)Filter: (departure_airport = $0)InitPlan 1 (returns $0)−> Seq Scan on airports_data ml  (cost=0.00..30.56 rows=1 wi...Filter: ((city −>> lang()) = 'Saint Petersburg'::text)
(5 rows)

InitPlan节点只执行一次,然后在主计划中使用改制而不是$0。

SELECT round(reltuples / s.n_distinct) AS rows
FROM pg_classJOIN pg_stats s ON s.tablename = relname
WHERE s.tablename = 'flights'AND s.attname = 'departure_airport';rows
−−−−−−2066

(1 row)如果所有数据均匀分布,则这些统计数据(连同最小值和最大值)足以进行准确的估计。不幸的是,这种估算不适用于非均匀分布,后者更为常见:

SELECT min(cnt), round(avg(cnt)) avg, max(cnt) FROM (SELECT departure_airport, count(*) cntFROM flights GROUP BY departure_airport
) t;min | avg  |  max
−−−−−+−−−−−−+−−−−−−−113 | 2066 | 20875
(1 row)

最常见的值

为提高非均匀分布的估算精度,分析器通常收集最常见值及其频率的统计信息。这些值存储在pg_stats的most_common_vals和most_common_freqs中。

以下是最常见飞机类型的此类统计数据示例:

SELECT most_common_vals AS mcv,left(most_common_freqs::text,60) || '...' AS mcf
FROM pg_stats
WHERE tablename = 'flights' AND attname = 'aircraft_code' \gx−[ RECORD 1 ]−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
mcv | {CN1,CR2,SU9,321,763,733,319,773}
mcf | {0.2783,0.27473333,0.25816667,0.059233334,0.038533334,0.0370...

估算“column = expression”的选择性非常简单:规划器只需从most_common_vals数组中获取一个值,然后将其乘以相同位置的频率most_common_freqs。

EXPLAIN SELECT * FROM flights WHERE aircraft_code = '733';QUERY PLAN
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−Seq Scan on flights  (cost=0.00..5309.84 rows=7957 width=63)Filter: (aircraft_code = '733'::bpchar)
(2 rows)SELECT round(reltuples * s.most_common_freqs[array_position((s.most_common_vals::text::text[]),'733')
])
FROM pg_classJOIN pg_stats s ON s.tablename = relname
WHERE s.tablename = 'flights'AND s.attname = 'aircraft_code';round
−−−−−−−7957
(1 row)

这个估算值将接近8263的真实值。

MCV列表也用于不等式的选择性估计:为了找到“column < value”的选择性,规划器搜索most_common_vals所有低于给定值的值,然后将他们的频率相加most_common_freqs。

当不同值数量较少时,公共值统计最有效。MCV数组的最大大小由default_statistics_target控制,该参数与分析期间控制行样本大小的参数相同。

某些情况下,将值(以及数组大小)增加到超出默认值将提供更加准确的统计。可以为每列设置此值:

ALTER TABLE ... ALTER COLUMN ... SET STATISTICS ...;

行样本大小也会增加,但仅限于表。公共值数组存储值本身,并且根据值的不同,可能会占用大量空间。这就是为什么超过1KB的值被排除在分析和统计之外的原因。它可以使pg_statistic大小在控制内,并且不会使规划器超载。无论如何,这么大的值通常是不同的,不包含在most_common_vals内。

原文

https://postgrespro.com/blog/pgsql/5969296

PG中的查询:2.统计--(1)相关推荐

  1. PostgreSQL中的查询:1.查询执行阶段

    PostgreSQL中的查询:1.查询执行阶段 开始关于PG内部执行机制的文章系列.这一篇侧重于查询计划和执行机制. 本系列包括: 1.查询执行阶段(本文) 2.统计数据 3.顺序扫描 4.索引扫描 ...

  2. mysql中实现分类统计查询的步骤_实验07:数据库的组合查询和统计查询

    实验6:数据库的组合查询和统计查询实验 本实验需要 4 学时. 一.实验目的 使学生熟练掌握SQL Server 查询分析器的使用方法,加深对SQL 和Transact-SQL 语言的查询语句的理解. ...

  3. avg最多用多少列 mysql_MySQL_MySQL中几种数据统计查询的基本使用教程,统计平均数 SELECT AVG() FROM 语 - phpStudy...

    MySQL中几种数据统计查询的基本使用教程 统计平均数SELECT AVG() FROM 语法用于从数据表中统计数据平均数. 语法: SELECT AVG(column) FROM tb_name 该 ...

  4. mysql中实现分类统计查询的步骤_在MySQL中如何进行分组统计查询

    昨天和大家分享了MySQL中,如何进行聚合函数及统计函数查询,若是不清楚的话,可以去看一下我的那个文章.今天继续和大家分享,在MySQL中如何进行分组统计查询,这个在实际应用中,也会经常运用到,比如以 ...

  5. 在excel中使用vba实现查询、统计系统

    excel里面有很多强大的公式,可以快速实现我们想要的结果.如果在vba中可以使用公式,可以大大减少编码,提升工作效率!这么强悍的功能让我们一起学习一下吧! 我们直接通过案例来学习公式在vba中的使用 ...

  6. Solr实现SQL的查询与统计--转载

    原文地址:http://shiyanjun.cn/archives/78.html Cloudera公司已经推出了基于Hadoop平台的查询统计分析工具Impala,只要熟悉SQL,就可以熟练地使用I ...

  7. 在 sql server 中,查询 数据库的大小 和 数据库中各表的大小

    2019独角兽企业重金招聘Python工程师标准>>> 在 sql server 中,查询 数据库的大小 和 数据库中各表的大小 其实本来只想找一个方法能查询一下 数据库 的大小,没 ...

  8. oracle关联分组查询,oracle中关联查询、分组查询

    高级查询 1.关联查询 作用:可以跨越多表查询 --查询出员工的名字和他所在部门的的名字 //古老的写法 select first_name,name from s_emp,s_dept where ...

  9. 数据库笔记02:查询与统计数据

    /***************************  第二单元:查询与统计数据 ***************************/ /* SELECT [DISTINCT][TOP n [ ...

最新文章

  1. Android配置build.gradle解锁更高逼格玩法(多版本共存、分服务器打包等)
  2. 1072 Gas Station (30 分)【难度: 中 / 知识点: Dijkstra + 枚举】
  3. 为LUKS加密的磁盘/分区做增量备份
  4. 大型企业多账号管理“安全心法”
  5. 邮箱验证 ——ACM
  6. day-16 jquery的DOM文档操作及bootstrap
  7. linux 流函数,标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流
  8. 计算机 图论基础知识,计算机基础知识
  9. PHP导出MySQL数据字典 Summer-Mysql-Dic
  10. java:高速排序算法与冒泡排序算法
  11. Java内存溢出的情况
  12. 分享一位大佬开发的驱动级的虚拟键盘鼠标,支持DD键鼠接口
  13. pcb 受潮_PCB受潮影响性能有什么好的处理方法?
  14. 等比矩阵求和-POJ3233
  15. 魔兽电影这么火,做成游戏一定很多人玩吧
  16. 机械转行程序员怎么样?
  17. j3455linux网卡不亮,J3455 ProxmoxVE v6.0-4 直通物理网卡教程
  18. [Ubuntu]vim中文乱码
  19. 粒子群算法的寻优算法-非线性函数极值寻优
  20. STemWin学习:关于窗口消息的基础知识

热门文章

  1. C#窗体控件简介_2
  2. java生成自增流水号,并从每月第一天重新清零计数将业务流水号添加到数据库(原创)...
  3. Toronto Research Chemicals 对乙酰氧基苯乙酮说明书
  4. PMP与ACP哪一个值得考?
  5. python海龟代码大全_海龟交易系统的Python完全版 | RiceQuant米筐量化社区 交易策略论坛...
  6. 2022-2028年中国汽车样车试制行业市场调研分析及发展规模预测报告
  7. illegal Key Size的解决方案
  8. Mac安装Hadoop(超级无敌宇宙爆炸详细)
  9. 本周大新闻|索尼PS VR2立项近7年;传腾讯将引进Quest 2
  10. 企业云盘满足什么需求呢