03
目录
PART
1
概述
CREATE TABLE test (x int, y int);INSERT INTO test SELECT mod(i,3),mod(i,30) FROM generate_series (1,10000) s(i);ANALYZE test;
PART
2
单列统计
单列统计信息可帮助Planner估算一个查询将返回的行数。
名称 | 类型 | 描述 |
schemaname | name | 模式名 |
tablename | name | 表名 |
attname | name | 列名 |
inherited | bool | 此行是否包括继承子列 |
null_frac | real | 列项中为空的比例 |
avg_width | integer | 列项的平均字节宽度 |
n_distinct | real | 列中可区分值的估计个数 |
most_common_vals | anyarray | 列中最常用值的一个列表 |
most_common_freqs | real[] | 最常用值的频率列表 |
histogram_bounds | anyarray | 将列值划分成大小接近的组的值列表 |
correlation | real | 物理行顺序和列值逻辑顺序之间的统计关联 |
most_common_elems | anyarray | 列值中最经常出现的非空元素列表 |
most_common_elem_freqs | real[] | 最常用元素值的频度列表 |
elem_count_histogram | real[] | 在列值中可区分非空元素值计数的一个直方图 |
通过查询pg_stats获取test表格中x列统计信息如下:
postgres=# select * from pg_stats where tablename ='test' and attname = 'x';-[ RECORD 1 ]----------+-----------------------schemaname | publictablename | testattname | xinherited | fnull_frac | 0avg_width | 4n_distinct | 3most_common_vals | {1,0,2}most_common_freqs | {0.3334,0.3333,0.3333}histogram_bounds |correlation | 0.3332most_common_elems |most_common_elem_freqs |elem_count_histogram |
为了使查询计划易于阅读,进行设置关闭每个查询的并行性。
postgres=# set max_parallel_workers_per_gather=0;
执行如下查询语句
postgres=# EXPLAIN ANALYZE SELECT * FROM test where x=1;QUERYPLAN----------------------------------------------------------------------------------------------Seq Scan ontest (cost=0.00..170.00 rows=3334width=8) (actual time=0.018..2.974 rows=3334 loops=1)Filter: (x =1)Rows Removedby Filter: 6666Planning Time:0.144 msExecution Time:3.438 ms(5 rows)postgres=# EXPLAIN ANALYZE SELECT * FROM test where y=1;QUERYPLAN--------------------------------------------------------------------------------------------Seq Scan ontest (cost=0.00..170.00 rows=334width=8) (actual time=0.028..4.026 rows=334 loops=1)Filter: (y =1)Rows Removedby Filter: 9666Planning Time:0.063 msExecution Time:4.115 ms(5 rows)
由于x列对3取模,所以,它的值只能是0,1,2,可以得出x=1的估计行数约为(1/3*10000)。通过结果能够看出,估计x=1的行数是3334,查询返回的实际行数是3334,非常准确。
Y列对30取模,其值是0~29,所以y=1的估计行数约为(1/30*10000)。通过结果能够看出,估计y=1的行数是334,查询返回的实际行数也是334,非常准确。
上面是条件查询中只包含一个条件的时候,那么让我们看一下,如果包含两个以上会怎么样呢?
postgres=# EXPLAIN ANALYZE SELECT * FROM test where x=1 and y=1;QUERYPLAN--------------------------------------------------------------------------------------------Seq Scan ontest (cost=0.00..195.00 rows=111width=8) (actual time=0.019..2.550 rows=334 loops=1)Filter: ((x =1) AND (y = 1))Rows Removedby Filter: 9666Planning Time:0.058 msExecution Time:2.604 ms(5 rows)
Planner估计的行数比实际行数少很多,这是由于Planner通常假定多个条件彼此独立,选择率会是每一列选择率相乘。
第一个列的选择率为 (1/3),第二个列的选择率为 (1/30)。那么两个独立条件的选择率= (1/3) * (1/30) ≈ 0.0111。
执行计划中的估计行数为0.0111*10000得到111,这就是rows=111的来源。但是,在该例子中,x和y列并不是独立的,y的值足以能够决定x的值,所以,当这两个条件同时存在的时候,我们只考虑y的选择范围即可。解决这个问题就需要使用CREATE STATISTICS来创建扩展统计的对象。
PART
3
扩展统计
有时候会看到查询缓慢,运行了错误的执行计划,很多是因为查询子句中包含多列。为此,PostgreSQL提供了扩展统计,可以创建扩展统计信息对象,也能解决多个非均匀分布列的查询,以指示服务器获得统计信息和更好计划。
使用CREATE STATISTICS创建统计信息对象,只是创建一个表示对统计信息相关的目录条目。实际的数据收集由ANALYZE执行(手动命令或后端自动分析),ANALYZE根据计算常规单列统计信息所用的相同表行样本来计算扩展统计信息,可以在pg_statistic_ext中检查收集的值。
pg_statistic_ext列包含:
名称 | 类型 | 描述 |
stxrelid | oid | 包含此对象描述的列的表 |
stxname | name | 统计对象的名称 |
stxnamespace | oid | 包含此统计信息对象的名称空间的OID |
stxowner | oid | 统计对象的所有者 |
stxkeys | int2vector | 统计对象覆盖的属性编号数组 |
stxkind | char[] | 包含已启用统计类型代码的数组 |
stxndistinct | pg_ndistinct | N-distinct计数 |
stxdependencies | pg_dependencies | 函数依赖性统计信息 |
以下小节介绍当前支持的扩展统计信息的种类。
函数依赖统计
最简单的扩展统计是函数依赖统计。如果知道a的值足够确定b的值,那就说明列b依赖于列a,即不存在两行有相同的a值但不同的b值。
函数依赖的存在直接影响某些查询中估计的准确性。为了向Planner通知函数依赖关系,ANALYZE可以收集跨列依赖关系的度量。此处我们建议仅为高度相关的列组件依赖统计信息,以避免在ANALYZE和以后的查询计划中产生不必要的开销。
回到我们单列统计的问题,x的值仅仅是y的1/10,所以x是函数依赖于y,也就是说,y的值足以决定x的值,不存在两行数据有相同的y但有不同的x。因此,在x列上的过滤筛选并没有移除任何行,我们创建一个统计对象去捕获这些列和运行分析(ANALYZE)所依赖的函数统计。
postgres=# CREATE STATISTICS depstats (dependencies)on x,y from test;CREATE STATISTICSpostgres=# ANALYZE test;ANALYZE
postgres=# EXPLAIN ANALYZE SELECT * FROM test where x=1 and y=1;QUERYPLAN--------------------------------------------------------------------------------------------Seq Scan ontest (cost=0.00..195.00 rows=334width=8) (actual time=0.017..2.822 rows=334 loops=1)Filter: ((x =1) AND (y = 1))Rows Removedby Filter: 9666Planning Time:0.173 msExecution Time:2.880 ms(5 rows)
postgres=# SELECT stxname, stxkeys, stxddependenciesFROM pg_statistic_ext join pg_statistic_ext_data on(oid = stxoid)WHERE stxname = 'depstats';stxname | stxkeys | stxddependencies----------+---------+----------------------depstats | 12 | {"2 => 1": 1.000000}(1 row)
N-Distinct统计
postgres=# EXPLAIN ANALYZE SELECT x,y,count(*) fromtest group by x, y;QUERYPLAN----------------------------------------------------------------------------------------------HashAggregate (cost=220.00..220.90 rows=90 width=16) (actual time=9.548..9.560 rows=30loops=1)Group Key: x,y-> Seq Scan on test (cost=0.00..145.00 rows=10000 width=8)(actual time=0.023..2.731 rows=10000 loops=1)Planning Time:0.100 msExecution Time:9.607 ms(5 rows)
postgres=# CREATE STATISTICS disstats(ndistinct) on x,y from test;CREATE STATISTICSpostgres=# ANALYZE test;ANALYZE
postgres=# EXPLAIN ANALYZE SELECT x,y,count(*) fromtest group by x, y;QUERYPLAN----------------------------------------------------------------------------------------------HashAggregate (cost=220.00..220.30 rows=30 width=16) (actual time=9.287..9.304 rows=30loops=1)Group Key: x,y-> Seq Scan on test (cost=0.00..145.00 rows=10000 width=8)(actual time=0.015..2.562 rows=10000 loops=1)Planning Time:0.133 msExecution Time:9.345 ms(5 rows)
postgres=# SELECT stxname, stxkeys, stxdndistinct FROM pg_statistic_ext joinpg_statistic_ext_data on (oid = stxoid) WHERE stxname = 'disstats';stxname | stxkeys | stxdndistinct----------+---------+---------------disstats | 12 | {"1, 2": 30}(1 row)
MCV Lists统计
名称 | 类型 | 描述 |
index | int | MCV列表中项目的索引 |
values | text[] | 存储在MCV项目中的值 |
nulls | boolean[] | 标识NULL值的标志 |
frequency | double precision | 此MCV项目的频率 |
base_frequency | double precision | 此MCV项目的基本频率 |
3.3.1
缺点
postgres=# DROP STATISTICS depstats;DROP STATISTICSpostgres=# DROP STATISTICS disstats;DROP STATISTICSpostgres=# CREATE STATISTICS mcvstats(mcv) ON x,y FROMtest;CREATE STATISTICSpostgres=# ANALYZE test;ANALYZE
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE x= 1 AND y = 1;QUERY PLAN--------------------------------------------------------------------------------------------Seq Scan ontest (cost=0.00..195.00 rows=334width=8) (actual time=0.032..3.729 rows=334 loops=1)Filter: ((x =1) AND (y = 1))Rows Removedby Filter: 9666Planning Time:0.296 msExecution Time:3.814 ms(5 rows)
3.3.2
优点
1、MCV列表中存储的是真实的值,能够判断一个组合是否包含
MCV List方式
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE x= 1 AND y = 2;QUERY PLAN----------------------------------------------------------------------------------------------Seq Scan ontest (cost=0.00..195.00 rows=1 width=8)(actual time=3.354..3.354 rows=0 loops=1)Filter: ((x =1) AND (y = 2))Rows Removedby Filter: 10000Planning Time:0.085 msExecution Time:3.371 ms(5 rows)
函数依赖方式
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE x= 1 AND y = 2;QUERY PLAN----------------------------------------------------------------------------------------------Seq Scan ontest (cost=0.00..195.00 rows=334width=8) (actual time=2.966..2.966 rows=0 loops=1)Filter: ((x =1) AND (y = 2))Rows Removedby Filter: 10000Planning Time:0.102 msExecution Time:2.981 ms(5 rows)
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE x> 3 AND y < 0;QUERY PLAN----------------------------------------------------------------------------------------------Seq Scan ontest (cost=0.00..195.00 rows=1 width=8)(actual time=3.177..3.177 rows=0 loops=1)Filter: ((x> 3) AND (y < 0))Rows Removedby Filter: 10000Planning Time:0.077 msExecution Time:3.194 ms(5 rows)
postgres=# SELECT m.* FROM pg_statistic_ext joinpg_statistic_ext_data on (oid = stxoid), pg_mcv_list_items(stxdmcv) m WHEREstxname = 'mcvstats';index |values | nulls | frequency |base_frequency-------+---------+-------+-----------+----------------0 | {2,2} | {f,f} | 0.0334 | 0.0111321 | {2,5} | {f,f} | 0.0334 | 0.0111322 | {2,8} | {f,f} | 0.0334 | 0.0111323 | {1, 10}| {f,f} | 0.0334 | 0.0111364 | {0,3} | {f,f} | 0.0334 | 0.0111325 | {0,6} | {f,f} | 0.0334 | 0.0111326 | {0,9} | {f,f} | 0.0334 | 0.0111327 | {1,1} | {f,f} | 0.0334 | 0.0111368 | {1,4} | {f,f} | 0.0334 | 0.0111369 | {1,7} | {f,f} | 0.0334 | 0.01113610 | {1, 22}| {f,f} | 0.0333 | 0.01110211 | {1, 25}| {f,f} | 0.0333 | 0.01110212 | {1, 28}| {f,f} | 0.0333 | 0.01110213 | {2, 11}| {f,f} | 0.0333 | 0.01109914 | {2, 14}| {f,f} | 0.0333 | 0.01109915 | {2, 17}| {f,f} | 0.0333 | 0.01109916 | {2, 20}| {f,f} | 0.0333 | 0.01109917 | {2, 23}| {f,f} | 0.0333 | 0.01109918 | {2, 26}| {f,f} | 0.0333 | 0.01109919 | {0,0} | {f,f} | 0.0333 | 0.01109920 | {2, 29}| {f,f} | 0.0333 | 0.01109921 | {0, 12}| {f,f} | 0.0333 | 0.01109922 | {0, 15}| {f,f} | 0.0333 | 0.01109923 | {0, 18}| {f,f} | 0.0333 | 0.01109924 | {0, 21}| {f,f} | 0.0333 | 0.01109925 | {0, 24}| {f,f} | 0.0333 | 0.01109926 | {0, 27}| {f,f} | 0.0333 | 0.01109927 | {1, 13}| {f,f} | 0.0333 | 0.01110228 | {1, 16}| {f,f} | 0.0333 | 0.01110229 | {1, 19}| {f,f} | 0.0333 | 0.011102(30 rows)
关于我们
中国开源软件推进联盟PostgreSQL分会(简称:PG分会)于2017年成立,由国内多家PG生态企业所共同发起,业务上接受工信部产业发展研究院指导。PG分会致力于构建PG产业生态,推动PG产学研用发展,是国内的一家PG行业协会组织。





