Greenplum中的统计信息
关于Greenplum数据库中的数据库统计信息
Greenplum数据库中通过ANALYZE命令收集的统计信息的概述。
统计信息是描述数据库中数据的元数据。 查询优化器需要最新的统计信息以便为查询选择最好的执行计划。 例如,如果一个查询连接两个表并且其中的一个必须被广播到所有的Segment,优化器可以选择两个表中较小的那一个来最小化网络流量。
优化器使用的统计信息由ANALYZE命令计算并且保存在系统目录中。有三种方式开始一个分析操作:
- 用户可以直接运行ANALYZE命令。
- 用户可以在数据库外部从命令行运行analyzedb管理工具。
- 当在没有统计信息的表上执行DML操作或者一个DML操作修改的行数超过指定阈值时,会触发一次自动的分析操作。
这些方法会在下面的小节中描述。VACUUM ANALYZE命令是另一种启动分析操作的方式,但是不鼓励使用它,因为清理和分析是两种不同目的的不同操作。
计算统计信息需要消耗时间和资源,因此Greenplum数据库通过在大型表的采样上计算统计信息来得到估计值。 在大部分情况下,默认的设置提供了足以为查询生成正确执行计划的信息。 如果产生的统计信息没有产生最优的查询执行计划,管理员可以调节配置参数通过提高样本尺寸或者系统目录中保存的统计信息粒度来产生更准确的统计信息。 产生更准确的统计信息需要CPU和存储代价并且不一定能产生更好的计划,因此重点是查看解释计划并且测试查询性能来确保额外的统计信息代价能导致更好的查询性能。
系统统计信息
表尺寸
查询规划器想要最小化执行查询所需的磁盘I/O和网络流量,它会使用必须被处理的行数以及查询必须访问的磁盘页面数的估计值。 用于生成这些估计值的数据是pg_class系统表列reltuples和relpages,它们分别包含上一次VACUUM或ANALYZE命令运行时的行数和页面数。 随着行被增加或删除,这些数字变得越来越不准确。 不过,总是可以从操作系统拿到准确的磁盘页面计数,因此只要reltuples与relpages的比例不发生显著变化,优化器就能够产生对选择正确的查询执行计划足够准确的行数估计。
*当reltuples列与SELECT COUNT()返回的行计数显著不同时,应该执行一次分析来更新统计信息。**
当一个REINDEX命令完成了重建一个索引时,relpages和reltuples列被设置为零。应该在基表上运行ANALYZE命令以更新这些列。
pg_statistic系统表和pg_stats视图
pg_statistic系统表保持在每个数据库表上最后一次ANALYZE操作的结果。其中为每一个表的每一列都有一行。行有下面的列:
starelid
该列所属的表或索引的对象ID。
staattnum
所描述列的编号,从1开始。
stainherit
如果值是true,统计信息不仅包括指定的relation,还包括继承的子列。
stanullfrac
列中为空的项的分数值。
stawidth
非空项的平均存储宽度,单位是字节。
stadistinct
一个正数,它是列中可区分值的数量估计。这个数字预计并不会随着行数变化。 一个负值是可区分值数量除以行数,也就是该列中有可区分值的行的比例取负值。 当可区分值数量随行数增加时使用这种形式。例如,一个唯一列的n_distinct值为-1.0。 平均宽度超过1024的列被认为是唯一的。
stakindN
一个代码数字,它表示存储在pg_statistic行第N个槽中的统计信息类型。
staopN
用来得到第N个槽中统计信息的操作符。例如,一个直方图槽会显示 < 操作符,它定义数据的排序顺序。
stanumbersN
float4数组,包含第N个槽的合适类型的数字统计信息,如果槽类型不涉及数字值则为NULL。
stavaluesN
第N个槽的合适类型的列数据值,如果该槽类型不存储任何数据值则为NULL。 每一个数组的元素值实际是指定列的数据类型,因此没有办法比使用anyarray更具体地定义这些列的类型。
为一个列收集的统计信息随着不同数据类型变化,因此pg_statistic表中存储适合于四个槽中数据类型的统计信息,每个槽由四个列组成。 例如,第一个槽通常包含一列的最常见值,它由列stakind1、staop1、stanumbers1和stavalues1组成。
stakindN列每个都包含一个数字代码,用于描述存储在其插槽中的统计信息的类型。 从1到99的stakind代码编号保留给核心PostgreSQL数据类型。 Greenplum数据库使用代码编号 1,2,3,4,5,和99。0的意思是槽未使用。 下面的表格描述了为三种代码存储的统计信息类型。
stakind代码 | 描述 |
---|---|
1 | 最常见值(MCV)槽staop包含”=”操作符的对象ID,它被用来决定值是否相同。stavalues包含一个数组,其中是该列中出现的K个最常见非空值。stanumbers包含stavalues数组中值的频度(总行数的分数)。值按照频度降序排序。因为数组是可变尺寸的,K可以由统计收集器选择。 要被加入到stavalues数组中,值必须出现超过一次。唯一列没有MCV槽。 |
2 | 直方图槽 – 描述标量数据的分布。staop是”<”操作符的对象ID,它描述排序顺序。stavalues包含M(其中M>=2)个非空值,它们将非空的列数据值划分成M-1个群体数量大致相等的箱子。第一个stavalues项是最小值而最后最后一个是最大值。stanumbers没有被使用且应该为NULL。如果也提供了一个最常见值的槽,那么该直方图描述的是将MCV数组中列出的值移除后的数据分布(在技术上的说法是一个压缩直方图)。 这允许更精确地表示一个有一些非常常见值的列的分布。 在一个只有一些可区分值的列中,有可能MCV列表就描述了整个数据群体,在这种情况下直方图缩小为空并且应该被省略。 |
3 | 相关关系槽 – 描述表元组物理顺序和列数据值顺序之间的相关关系。staop是”<”操作符的对象ID。与直方图一样,理论上可能会出现多于一项。stavalues未被使用并且应该为空。stanumbers包含一个单项,它是数据值的序列和它们实际元组位置序列之间的相关系数。系数范围从+1到-1。 |
4 | 最常见的元素插槽 - 类似于最常见值(MCV)插槽,除了它存储列值的最常见非空元素。 当列数据类型是数组或具有可识别元素的其他类型(例如,tsvector)时,这非常有用。staop包含适合于元素类型的相等运算符。stavalues包含最常见的元素值。stanumbers包含共同的元素频率。频率测量为元素值出现的非空行的分数,而不是所有行的频率。 此外,值将按元素类型的默认顺序排序(以支持特定值的二分查找)。 由于这会将最小和最大频率放在不稳定点上,因此有两个额外的stanumbers成员可以保存最小和最大频率的副本。 可选地,可以存在保持空元素的频率的第三额外成员(频率以相同的术语表示:包含至少一个空元素的非空行的分数)。 如果省略此成员,则假定该列不包含NULL元素。Note: 注意:对于tsvector列,stavalues元素的类型为text,即使它们在tsvector中的表示不完全是文本。 |
5 | 不同的元素计数直方图槽 - 描述了数组类型列的每一行中存在的不同元素值的数量的分布。 仅考虑非空行,并且仅考虑非空元素。staop包含适合于元素类型的相等运算符。stavalues未使用且应为NULL。stanumbers包含有关不同元素的信息。 stanumbers的最后一个成员是所有非空行上的不同元素值的平均计数。 前面的M(其中M> = 2)成员形成直方图,该直方图将不同元素计数的群体划分为大致相等群体的M-1个区间。 第一个是观察到的最小计数,最后一个是最大值。 |
99 | 超级日志槽 - 对于分区表的子叶分区,存储为采样数据创建的超级日志计数器。 hyperloglog_counter数据结构转换为bytea并存储在pg_statistic目录表的stavalues5插槽中。 |
pg_stats视图以一种友好的格式表示pg_statistic的内容。pg_stats视图有下列列:
schemaname
包含该表的方案名称。
tablename
该表的名称。
attname
这行所描述的列名。
inherited
如果为true,统计信息包含继承的子列。
null_frac
为空的列项所占的比例。
avg_width
该列中项的平均存储宽度(以字节为单位),计算方法是avg(pg_column_size(column_name))。
n_distinct
一个正数是该列中可区分值的数量估计。这个数字预计并不会随着行数变化。 一个负值是可区分值数量除以行数,也就是该列中有可区分值的行的比例取负值。 当可区分值数量随行数增加时使用这种形式。例如,一个唯一列的n_distinct值为-1.0。 平均宽度超过1024的列被认为是唯一的。
most_common_vals
包含该列中最常见值的数组,如果没有值看起来更常见则为空。 如果n_distinct列为-1,则most_common_vals为空。 这个数组的长度小于实际的可区分列值的数量或者default_statistics_target配置参数的值。 对一个列可以使用ALTER TABLE table SET COLUMN column SET STATISTICS N覆盖值的数量。
most_common_freqs
包含most_common_vals数组中值的频率。它是一个值的出现次数除以总行数。 这个数组和most_common_vals数组的长度相等。如果most_common_vals为空,则它也为空。
histogram_bounds
一个值数组,它把列值划分成大约相同尺寸的分组。只有对该列有一个max()聚集函数时才能定义直方图。 直方图中分组的数量等于most_common_vals数组的尺寸。
correlation
Greenplum数据库不计算相关关系统计信息。
most_common_elems
包含最常见元素值的数组。
most_common_elem_freqs
包含公共元素频率的数组。
elem_count_histogram
一个数组,描述数组类型列的每一行中存在的不同元素值的数量的分布。
新创建的表和索引没有统计信息。可以用gp_stats_missing视图检查缺少统计信息的表,该视图位于gp_toolkit方案中:
1 | SELECT * from gp_toolkit.gp_stats_missing; |
采样
在为大型表计算统计信息时,Greenplum数据库通过采样基表来创建一个较小的表。如果表被分过区,会从所有的分区取得抽样。
更新统计信息
运行不带参数的ANALYZE会为数据库中的所有表更新统计信息。 这可能会耗费非常长的时间,因此更好的方式是在数据被改变后有选择地分析表。 用户还可以分析一个表中的列子集,例如连接中、WHERE子句、SORT子句、GROUP BY子句或者HAVING子句中用到的列。
如果采样中包含空页面,分析一个严重膨胀的表可能生成不好的统计信息,因此最好的做法是在分析一个膨胀了的表之前先清理它。
运行ANALYZE命令的详情请见Greenplum数据库参考指南中的SQL命令参考。
运行analyzedb命令的细节请参考Greenplum数据库管理工具参考。
分析分区表
当ANALYZE命令被运行在一个分区表上时,它会逐个分析每一个叶子层子分区。 用户可以只在新的或者更改过的分区表上运行ANALYZE以避免分析没有变化过的分区。
analyzedb命令行工具会自动跳过未更改的分区。它还会运行并发会话,这样它可以并发地分析几个分区。 默认它会运行五个会话,但会话的数量可以用-p命令行选项设置为1至10。 analyzedb每次运行时,它会在Master数据目录中的db_analyze目录下为追加优化表和分区保存状态信息。 下一次它运行时,analyzedb会把每个表的当前状态与其保存状态相比较,并且跳过没有更改的表或分区。堆表总是会被分析。
如果GPORCA被启用(默认),用户还需要运行ANALYZE ROOTPARTITION来刷新根分区的统计信息。 GPORCA要求在分区表根层的统计信息。传统优化器不使用这些统计信息。
分析分区表的时间类似于分析具有相同数据的非分区表的时间,因为ANALYZE ROOTPARTITION不收集叶分区上的统计信息(仅对数据进行采样)。 analyzedb工具默认会更新根分区统计信息
Greenplum数据库服务器配置参数optimizer_analyze_root_partition会影响何时在分区表的根分区上收集统计信息。 如果参数为on(缺省值),则在运行ANALYZE时,不需要ROOTPARTITION关键字来收集根分区的统计信息。 在根分区上运行ANALYZE时,或者在分区表的子叶子分区上运行ANALYZE并且其他子叶子分区具有统计信息时,将收集根分区统计信息。 如果参数关闭,则必须运行ANALYZE ROOTPARTITION以收集根分区统计信息。
如果您不打算使用GPORCA对分区表执行查询(将服务器配置参数optimizer设置为关闭), 则还可以将服务器配置参数optimizer_analyze_root_partition设置为off,以限制ANALYZE更新根分区统计信息的时间。
配置统计信息
有几个选项可以配置Greenplum数据库的统计信息收集。
统计信息目标
统计信息目标是一个列的most_common_vals、most_common_freqs以及histogram_bounds数组的尺寸。 默认情况下,目标是25。默认目标可以通过设置一个服务器配置参数来更改,对任意列可以使用ALTER TABLE命令设置目标。 较大的值会增加执行ANALYZE所需的时间,但是可能会增加传统查询优化器(规划器)估计值的质量。
可以通过设置default_statistics_target服务器配置参数将系统的默认统计信息目标设置为一个不同的值。 默认值通常是足够的,用户只应该在测试证明新的目标能够改进查询计划时才调整目标。 例如,要将默认的统计信息目标从100增加到150,用户可以使用gpconfig工具:
1 | gpconfig -c default_statistics_target -v 150 |
可以使用ALTER TABLE命令设置各列的统计信息目标。 例如,某些查询可以通过增加某些列的目标来改进,尤其是具有不规则分布的列。 对于从不参与查询优化的列,您可以将目标设置为零。 当目标是0,ANALYZE会忽略该列。 例如,以下ALTER TABLE命令将emp表中notes列的统计信息目标设置为零:
1 | ALTER TABLE emp ALTER COLUMN notes SET STATISTICS 0; |
统计信息目标可以被设置为0到1000,或者把它设置为-1以回到使用系统的默认统计信息目标。
在父分区表上设置统计信息目标会影响子分区。 如果在父表上把某些列的统计信息目标设置为0,对所有子分区也会把相同列的统计信息目标设置为0。 不过,如果用户后来增加或者交换了另一个子分区,新的子分区将会使用默认统计信息目标或者之前的统计信息目标(在交换的情况下)。 因此,如果用户增加或者交换子分区,应该在新的子表上设置统计信息目标。
自动统计收集
Greenplum数据库可以被设置为在没有统计信息或者在执行特定操作后发生显著改变的表上自动地运行ANALYZE。 对于分区表,自动统计收集只在操作直接运行在叶子表上时才被触发,然后也只会分析这个叶子表。
自动统计收集有三种模式:
- none 禁用自动统计收集。
- 当任意一个CREATE TABLE AS SELECT、INSERT或者COPY命令在没有现存统计信息的表上执行时,on_no_stats模式会为该表触发一次分析操作。
- 当任意一个CREATE TABLE AS SELECT、UPDATE、DELETE、INSERT或者COPY命令在表上执行并且被影响的行数超过gp_autostats_on_change_threshold配置参数所定义的阈值时, on_change模式会为该表触发一次分析操作
对于发生在一个过程语言函数内的命令以及在一个函数之外执行的命令,自动统计收集模式是单独设置的:
- gp_autostats_mode配置参数控制函数外自动统计收集的行为并且默认被设置为on_no_stats。
- gp_autostats_mode_in_functions参数控制表操作在一个过程语言函数内执行时的行为并且默认被设置为none。
通过on_change模式,只有受影响的行数超过gp_autostats_on_change_threshold配置参数所定义的阈值时才会触发ANALYZE。 这个参数的默认值是一个非常高的值2147483647(21亿),它实际上禁用了自动统计收集。 用户必须将该阈值设置得较低来启用它。on_change模式可能会触发大型的、预期之外的分析操作,它们可能会中断系统,因此不推荐在全局范围内设置它。 在一个会话中它可能会有用,例如在一次装载后自动分析一个表。
要在函数之外禁用自动统计收集,将gp_autostats_mode参数设置为none:
1 | gpconfigure -c gp_autostats_mode -v none |
要在函数中为没有统计信息的表启用自动统计收集,将gp_autostats_mode_in_functions改成on_no_stats:
1 | gpconfigure -c gp_autostats_mode_in_functions -v on_no_stats |
如果想要记录自动统计收集操作,可将log_autostats系统配置参数设置为on。
用ANALYZE更新统计信息
良好查询性能的最重要的先决条件是从表的正确统计信息开始。用ANALYZE语句更新统计信息 让查询规划器能生成最优的查询计划。当表被分析时,有关数据的信息被存储在系统目录表中。如果存储的信息过时, 规划器可能会生成低效的执行计划。
说明
ANALYZE是Greenplum提供的收集统计信息的命令。
ANALYZE支持三种粒度,列,表,库,如下:
1 2 3 4 | CREATE TABLE foo (id int NOT NULL, bar text NOT NULL) DISTRIBUTED BY (id); // 创建测试表foo ANALYZE foo(bar); // 只搜集bar列的统计信息 ANALYZE foo; // 搜集foo表的统计信息 ANALYZE; // 搜集当前库所有表的统计信息,需要有权限才行 |
限制
ANALYZE会给目标表加SHARE UPDATE EXCLUSIVE锁,也就是与UPDATE,DELETE,还有DDL语句冲突。
速度
ANALYZE是一种采样统计算法,通常不会扫描表中所有的数据,但是对于大表,也仍会消耗一定的时间和计算资源。
采样统计会有精度的问题,因此Greenplum也提供了一个参数default_statistics_target,调整采样的比例。简单说来,这个值设置得越大,采样的数量就越多,准确性就越高,但是消耗的时间和资源也越多。
直接修改服务器的参数会影响整个集群,通常不建议这样操作。如果确实有需要,可以尝试只修改某列的对应参数,如下:
1 | ALTER TABLE {table_name} ALTER COLUMN {col_name} SET STATISTICS {-1|0-1000}; |
有选择地生成统计信息
不带参数运行ANALYZE 会为数据库中所有的表更新统计信息。这样操作运行时间可能会非常长,因此不推荐这样做。当数据被改变时,使用者 应该有选择地ANALYZE表或者使用analyzedb工具。
在大型表上运行ANALYZE可能需要很长时间。如果在非常大的表的所有列上运行ANALYZE 行不通,使用者可以只使用ANALYZE table(column,…)为选择的列生成统计信息。确保包括用在 连接、WHERE子句、SORT子句、GROUP BY子句或者 HAVING子句中的列都被收集了统计信息。
对于一个分区表,使用者可以只在更改过的分区(例如,使用者增加一个分区)上运行ANALYZE。 注意对于分区表,使用者可以在父(主)表上或者叶子节点(实际存储数据和统计信息的分区文件)上运行ANALYZE。 子分区表的中间文件没有存储数据或统计信息,因此在其上运行ANALYZE没有效果。使用者可以在 pg_partitions系统目录中寻找分区表的名字:
1 | SELECT partitiontablename from pg_partitions WHERE tablename='parent_table; |
提升统计信息质量
在生成统计信息所花的时间和统计信息的质量或者准确性之间存在着权衡。
为了允许大型表能在合理的时间内被分析完,ANALYZE会对表内容做随机采样而不是检查每一行。 要对所有表列增加采样,可调整default_statistics_target配置参数。其目标值取值范围从 1到1000,默认的目标值是100。default_statistics_target变量默认会被应用到所有的列。 更大的目标值会增加执行ANALYZE所需的时间,但是可以提升查询规划器的评估质量。对于带有不规则数据模式的列尤 其如此。default_statistics_target可以在master或者会话级别设置,并且要求重新载入 配置。
何时运行ANALYZE
在下列时机运行ANALYZE:
- 装载数据后;
- CREATE INDEX操作后;
- 在显著更改底层数据的INSERT、UPDATE以及DELETE 操作之后。
ANALYZE仅在表上要求一个读锁,因此它可以与其他数据库活动并行运行。但不要在执行 装载、INSERT、UPDATE、DELETE以及CREATE INDEX 操作期间运行ANALYZE。
配置统计信息自动收集
gp_autostats_mode配置参数与gp_autostats_on_change_threshold 参数一起决定何时触发自动分析操作。当自动统计信息收集被触发时,规划器会为查询增加一个ANALYZE 步骤。
gp_autostats_mode默认为on_no_stats,这会为任何没有统计信息的表上的CREATE TABLE AS SELECT、INSERT或者COPY 操作触发统计信息收集。实际上默认情况下,我们对空表写入数据后, Greenplum 也会自动帮我们收集统计信息,不过之后再写入数据,就需要手动收集统计信息了。
把gp_autostats_mode设置为on_change时,只有当受影响的行数超过由 gp_autostats_on_change_threshold定义的阈值时才会触发统计信息收集,该阈值参数的默认值为2147483647(约21亿)。on_change设置下能触发自动统计信息收集的操作有: CREATE TABLE AS SELECT、UPDATE、DELETE、INSERT以及COPY。
将gp_autostats_mode设置为none会禁用自动统计信息收集。
对于分区表,如果数据从分区表的顶层父表插入,则自动统计信息收集不会被触发。但是如果数据直接被插入在 分区表的叶子表(存储数据的地方)中,则自动统计信息收集会被触发。
统计信息去了哪里
pg_class
表的大小是统计信息里面最直观,也几乎是最重要的,这个信息是放在pg_catalog.pg_class系统表中,reltuples代表元组数(行数),relpages代表实际占用的page数目(Greenplum中一个page为32KB)。
需要注意以下3点
reltuples不是准确值,获取表的准确行数还是需要count。
reltuples和relpages需要通过ANALYZE进行收集,对于已有数据的表,系统不会自动更新。
reltuples和relpages不一定能对齐,比如条数看起来不多的表,实际占用的page数目很大,这种一般是由于数据膨胀(bloat)造成,这时候需要vacuum等操作。
pg_statistic
关于列的统计信息都是存放在pg_catalog.pg_statistic系统表中。其中表的每一列(如果有统计)都会有一行对应的数据。了解并掌握pg_statistic的内容,对于深入理解查询优化非常重要。
列的统计信息内容很丰富,但是目的都是让优化器估算出,一个查询条件,能够过滤多少数据。
以下列举了pg_statistic的重要字段:
列名 | 说明 |
---|---|
stanullfrac | null值的比例 |
stawidth | 列值的平均宽度 |
stadistinct | 大于0代表去重之后的个数 小于0其绝对值代表去重之后个数的比例 等于0代表无意义 |
stakindN | #define STATISTIC_KIND_MCV 1 #define STATISTIC_KIND_HISTOGRAM 2 #define STATISTIC_KIND_CORRELATION 3 #define STATISTIC_KIND_MCELEM 4 #define STATISTIC_KIND_DECHIST 5 #define STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM 6 #define STATISTIC_KIND_BOUNDS_HISTOGRAM 7 #define STATISTIC_KIND_HLL 99 |
ANALYZE
收集有关一个数据库的统计信息。
概要
1 2 3 | ANALYZE [VERBOSE] [table [ (column [, ...] ) ]] ANALYZE [VERBOSE] {root_partition|leaf_partition} [ (column [, ...] )] ANALYZE [VERBOSE] ROOTPARTITION {ALL | root_partition [ (column [, ...] )]} |
描述
ANALYZE收集有关数据库中表内容的统计信息,并将结果存储在系统表pg_statistic中。 随后,Greenplum数据库使用这些统计信息来帮助确定最有效的查询执行计划。 有关收集的表统计信息的信息,请参阅Notes。
如果不使用任何参数,则ANALYZE会收集当前数据库中每个表的统计信息。 您可以指定表名称以收集单个表的统计信息。 您可以指定一组列名,在这种情况下,仅收集这些列的统计信息。
ANALYZE不会收集外部表的统计信息。
对于分区表,ANALYZE在叶子分区上收集其他统计信息,即HyperLogLog(HLL)统计信息。 HLL统计信息用于得出针对分区表的查询的不同值(NDV)数量。
- 当汇总多个叶子分区的NDV估计值时,HLL统计信息比标准表统计信息生成更准确的NDV估计值。
- 更新HLL统计信息时,仅在已更改的叶子分区上才需要ANALYZE操作。 例如,如果叶子子分区数据已更改,或者叶子子分区已与另一个表交换,则ANALYZE是必需的。 有关更新分区表统计信息的更多信息,请参见Notes。
Important: 如果要在启用GPORCA的分区表上执行查询(默认设置),则必须使用ANALYZE或ANALYZE ROOTPARTITION命令在分区表的根分区上收集统计信息。 有关收集分区表上的统计信息以及何时需要ROOTPARTITION关键字的信息,请参阅Notes。 有关GPORCA的信息,请参阅中的GPORCA概述。
Note: 您还可以使用Greenplum数据库实用程序analyzedb更新表统计信息。 analyzedb 可以同时更新多个表的统计信息。 该实用程序还可以检查表统计信息并仅在统计信息不是当前统计信息或不存在时更新统计信息。 有关该实用程序的信息,请参阅Greenplum Database Utility Guide。
参数
{ root_partition | leaf_partition } [ (column [, …] ) ]
收集分区表的统计信息,包括HLL统计信息。 HLL统计信息仅在叶子分区上收集。
ANALYZE root_partition, 收集所有叶子分区和根分区的统计信息。
ANALYZE leaf_partition, 收集有关叶子分区的统计信息。
默认情况下,如果指定叶子分区,并且所有其他叶子分区都具有统计信息,则ANALYZE更新根分区统计信息。 如果不是所有叶子子分区都具有统计信息,则ANALYZE记录有关没有统计信息的叶子子分区的信息。 有关何时收集根分区统计信息的信息,请参阅Notes。
ROOTPARTITION [ALL]
仅基于分区表中的数据收集分区表的根分区上的统计信息。 如果可能,ANALYZE使用叶子分区统计信息生成根分区统计信息。 否则,ANALYZE通过对叶子分区数据进行采样来收集统计信息。 未在叶子分区上收集统计信息,仅对数据进行采样。 不会收集HLL统计信息。
有关何时需要ROOTPARTITION关键字的信息,请参阅Notes。
指定ROOTPARTITION时,必须指定ALL或分区表的名称。
如果将ROOTPARTITION指定为ALL,则Greenplum Database会收集数据库中所有分区表的根分区的统计信息。如果数据库中没有分区表,则会返回一条消息,指出没有分区表。对于不是分区表的表,不会收集统计信息。
如果使用ROOTPARTITION指定表名,并且该表不是分区表,则不会为该表收集任何统计信息,并且会返回警告消息。
ROOTPARTITION子句不适用于VACUUM ANALYZE。 VACUUM ANALYZE ROOTPARTITION命令返回错误。
运行ANALYZE ROOTPARTITION的时间类似于分析具有相同数据的非分区表的时间,因为ANALYZE ROOTPARTITION仅采样叶子分区数据。
对于分区表 sales_curr_yr,此示例命令仅在分区表的根分区上收集统计信息。ANALYZE ROOTPARTITION sales_curr_yr; ;
此示例ANALYZE命令收集有关数据库中所有分区表的根分区的统计信息。
1 | ANALYZE ROOTPARTITION ALL; |
VERBOSE
启用显示进度消息。 启用显示进度消息。 指定时,ANALYZE 发出此信息
- 正在处理的表。
- 执行该查询以生成示例表。
- 要为其计算统计信息的列。
- 发出以收集单个列的不同统计信息的查询。
- 收集的统计信息。
table
要分析的特定表的名称(可能是模式限定的)。 如果省略,则分析当前数据库中的所有常规表(而不是外部表)。
column
要分析的特定列的名称。 默认为所有列。
注意
仅在明确选择外表时才进行分析。 并非所有外部数据包装器都支持ANALYZE。 如果表的包装器不支持ANALYZE,则该命令将显示警告并且不执行任何操作。
最好定期或在对表内容进行重大更改之后立即运行ANALYZE。 准确的统计信息有助于Greenplum数据库选择最合适的查询计划,从而提高查询处理的速度。 只读数据库的常见策略是在一天的低使用时间内每天运行一次 VACUUM 和ANALYZE。 (如果有大量更新活动,这是不够的。)您可以使用gp_toolkit模式中的gp_stats_missing视图来检查缺少统计信息的表:
1 | SELECT * from gp_toolkit.gp_stats_missing; |
ANALYZE要求对目标表进行SHARE UPDATE EXCLUSIVE锁定。 此锁与以下锁冲突:SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE。
如果您在不包含数据的表上运行ANALYZE,则不会为该表收集统计信息。 例如,如果您对具有统计信息的表执行TRUNCATE操作,然后对该表运行ANALYZE,则统计信息不会更改。
对于分区表,如果分区表具有大量已分析的分区,而只有几个叶子分区具有分区,则指定要分析的表部分,根分区或子分区(叶子分区表)可能会有用 改变了。
Note: 当您使用CREATE TABLE命令创建分区表时,Greenplum数据库将创建您指定的表(根分区或父表),并根据您指定的分区层次结构(子表)创建表层次结构。
在根分区表上运行ANALYZE时,将收集所有叶子分区的统计信息。 叶子子分区是Greenplum数据库创建供子表使用的子表层次结构中的最低级表。
在叶子分区上运行ANALYZE时,仅收集该叶子分区和根分区的统计信息。 如果叶子分区中的数据已更改(例如,您对叶子子分区数据进行了重大更新或交换了叶子子分区),则可以在叶子子分区上运行ANALYZE来收集表统计信息。 默认情况下,如果所有其他叶子分区都具有统计信息,则该命令将更新根分区统计信息。
例如,如果您在具有大量分区的分区表上收集统计信息,然后仅在几个叶子分区中更新数据,则可以仅在那些分区上运行ANALYZE来更新分区的统计信息和根分区的统计信息。
在不是叶子分区的子表上运行ANALYZE时,不会收集统计信息。
例如,您可以创建一个分区表,其中包含2006年至2016年的分区以及每年每个月的子分区。 如果您在2013年的子表上运行ANALYZE,则不会收集任何统计信息。 如果您在2013年3月在叶子分区上运行ANALYZE,则仅收集该叶子分区的统计信息。
对于包含已被交换以使用外部表的叶子分区的分区表,ANALYZE不会收集外部表分区的统计信息:
- 如果在外部表分区上运行ANALYZE,则不会分析该分区。
- 如果在根分区上运行ANALYZE或ANALYZE ROOTPARTITION,则不对外部表分区进行采样,并且根表统计信息不包括外部表分区。
- 如果指定了VERBOSE子句,则会显示一条参考消息:skipping external table。
Greenplum数据库服务器配置参数optimizer_analyze_root_partition影响何时在分区表的根分区上收集统计信息。 如果该参数为on(默认值),则在运行ANALYZE时,不需要ROOTPARTITION关键字来收集根分区上的统计信息。 在根分区上运行ANALYZE或在分区表的子叶分区上运行ANALYZE且其他子叶分区具有统计信息时,将收集根分区统计信息。 如果该参数是off,则必须运行ANALZYE ROOTPARTITION来收集根分区统计信息。
ANALYZE收集的统计信息通常包括每列中一些最常用值的列表以及显示每列中近似数据分布的直方图。 如果ANALYZE认为它们不重要(例如,在唯一键列中没有公共值),或者列数据类型不支持适当的运算符,则可以忽略其中一个或两个。
对于大型表,ANALYZE会从表内容中随机抽取一个样本,而不是检查每一行。这样就可以在很短的时间内分析非常大的表。但是请注意,统计信息仅是近似的,并且每次运行ANALYZE都会略有变化,即使实际的表内容没有变化。这可能会导致EXPLAIN所显示的计划者估算成本发生细微变化。在极少数情况下,这种不确定性将导致查询优化器在ANALYZE运行之间选择不同的查询计划。为了避免这种情况,请通过调整default_statistics_target配置参数来提高ANALYZE收集的统计信息的数量,或者通过使用ALTER TABLE … ALTER COLUMN … SET (n_distinct …)(请参阅ALTER TABLE)。目标值设置最常用值列表中的最大条目数和直方图中的最大bin数。默认目标值是100,但是可以向上或向下调整该值以权衡规划器估计的准确性与ANALYZEv所花费的时间以及pg_statistic中占用的空间量。特别是,将统计目标设置为零会禁用该列的统计收集。对于从未用作查询的WHERE, GROUP BY或ORDER BY子句一部分的列,执行此操作可能很有用,因为计划器将不会使用此类列的统计信息。
要分析的列中最大的统计信息目标确定为准备统计信息而采样的表行数。 增加目标会导致进行ANALYZE所需的时间和空间成比例增加。
ANALYZE估计的值之一是出现在每列中的不同值的数量。 因为仅检查了行的子集,所以即使使用最大可能的统计目标,此估计有时也可能非常不准确。 如果此错误导致查询计划不正确,则可以手动确定更准确的值,然后与ALTER TABLE … ALTER COLUMN … SET STATISTICS DISTINCT一起安装(请参阅ALTER TABLE)。
当Greenplum数据库执行ANALYZE操作以收集表的统计信息并检测到所有采样的表数据页均为空(不包含有效数据)时,Greenplum数据库将显示一条消息,指出应该执行VACUUM FULL操作。 如果采样页为空,则表统计信息将不准确。 对表进行大量更改(例如删除大量行)后,页面将变为空。 VACUUM FULL操作可删除空白页,并允许ANALYZE操作收集准确的统计信息。
如果该表没有统计信息,则服务器配置参数gp_enable_relsize_collection将控制Postgres查询优化器使用默认统计信息文件还是使用pg_relation_size函数估计表的大小。 默认情况下,如果统计信息不可用,Postgres优化器将使用默认的统计信息文件来估计行数。
示例
收集表mytable的统计信息:
1 | ANALYZE mytable; |
兼容性
SQL标准中没有ANALYZE语句。
另见
ALTER TABLE, EXPLAIN, VACUUM, analyzedb utility in the Greenplum Database Utility Guide.
总结
1、缺失统计信息的表可以通过如下SQL查询
1 | SELECT * from gp_toolkit.gp_stats_missing; |
2、ANALYZE会给目标表加SHARE UPDATE EXCLUSIVE
锁,也就是与UPDATE,DELETE,还有DDL语句冲突。
3、有2个参数可以用来调整自动化收集统计信息的时机,gp_autostats_mode 和 gp_autostats_on_change_threshold。gp_autostats_mode 默认是 on_no_stats,也就是如果表还没有统计信息,这时候写入数据(CREATE TABLE AS SELECT、INSERT、COPY )会导致自动收集,这之后,无论表数据变化多大,都只能手动收集了。如果将 gp_autostats_mode 修改为 on_change ,就是在数据变化量达到 gp_autostats_on_change_threshold(默认21亿) 参数配置的量之后,系统就会自动收集统计信息。
参考
https://www.bookstack.cn/read/greenplum-admin_guide-6.0-zh/57366ba81afb7d6c.md