2006年(26)
分类:
2006-03-21 13:21:57
现在,几乎所有重要数据库都使用某种方法来更新目录统计信息,以便为其优化器提供可能的最佳信息。可以将优化器视为一个勘探器,正在系统中的数据所代表的大山中进行定位。目录统计信息的更新将为优化器提供最新的地图,以便在整个地形中快速定位。
DB2 优化器使用目录统计信息来确定最佳的访问路径,而更新这些目录统计信息所采用的主要方法就是运行 RUNSTATS 实用程序。当用户表中发生数据修改时,目录统计信息表不会自动被修改。必须在表和索引上执行 RUNSTATS 命令,用最新的信息更新目录表中的列。
可以查询下列目录列,确定是否在表和索引上执行了 RUNSTATS:
在第一次将数据载入到表中之后,就无法避免地要对表进行更新、删除和插入等形式的修改。可以添加或删除索引。特定列中的数据分布可能随时间发生改变。目录中不会反映这些修改,除非在这些表和索引上执行 RUNSTATS。一段时间之后,随着表和数据发生更改,目录统计信息可能会过时。
执行 RUNSTATS 是很重要的,但是频繁地运行可能会带来问题。对于较小的表,发出 RUNSTATS 命令将是一项普通任务。然而,随着表的增长,完成 RUNSTATS 命令将占用更多时间、CPU 和内存资源。最终,您必须考虑分配更多时间和资源来运行 RUNSTATS 与不执行该命令的潜在性能下降之间的平衡。通常只在关键查询的速度开始减慢时,管理员才会对 RUNSTATS 给予适当的注意。您可以通过制定高效、有效收集统计信息的策略,避免未经思考就调优查询和执行 RUNSTATS。
理论上,应该在下列情况下对表和索引执行 RUNSTATS:
您可以通过比较查询 RUNSTATS 之前和之后的 EXPLAIN 输出,来确定运行 RUNSTATS 对于访问计划的影响。
完成每一条 RUNSTATS 语句之后,您都应该执行显式的 COMMIT WORK。COMMIT 将释放锁,并避免在收集多个表的统计信息时填写日志。
在用 RUNSTATS 收集了统计信息之后,要使用 BIND 命令或 REBIND 命令重新绑定包含了静态 SQL 的包(并可以选择重新解释其语句)。 db2rbind
命令可用于重新绑定数据库中的所有包。使用 FLUSH PACKAGE 命令来删除包缓存器中当前所有缓存的动态 SQL 语句,并强制隐式地编译下一请求。
注意:在 RUNSTATS 语法中,必须使用全限定的表名 schema.table-name
和全限定的索引名 schema.index-name
。您可以在所有列上,或者仅仅在某些列或列组(除了 LONG 和 LOB 列)上执行 RUNSTATS。如果没有指定特定列的子句,系统则会使用默认的 ON ALL COLUMNS 子句。
当您已确定表中包含不是统一分布的数据时,可以运行包含 WITH DISTRIBUTION 子句的 RUNSTATS。目录统计信息表通常包含关于表中最高和最低值的信息,而优化器假定数据值是在两个端点值之间均匀分布的。然而,如果数据值彼此之间差异较大,或者群集在某些点上,或者是碰到许多重复的数据值,那么优化器就无法选择一个最佳的访问路径,除非收集了分布统计信息。使用 WITH DISTRIBUTION 子句还可以帮助查询处理没有参数标志符(parameter marker)或主机变量的谓词,因为优化器仍然不知道运行时的值是有许多行,还是只有少数行。
在执行包含 WITH DISTRIBUTION 子句的 RUNSTATS 时,会根据 RUNSTATS 命令中给定的选项选择一组频率(frequency)和分位数(quantile)的统计信息。
RUNSTATS 收集两种类型的数据分布统计信息:频率统计信息和分位数统计信息。
频率统计信息的默认值由 num_freqvalues 数据库配置参数控制,该值提供了重复最多的列和数据值的信息。其默认值是 10,建议将这个值设置在 10 到 100 之间。如果将 num_freqvalues 设置为零,则不保留任何频率值的统计信息。
分位数统计信息的默认值由 num_quantiles 数据库配置参数控制,该值提供了数据值对于其他值而言是如何分布的有关信息。 num_quantiles 数据库配置参数指定应将列数据值分成的组数。其默认值是 20,建议将该值设置在 20 到 50 之间。如果将这个参数设置为零或“1”,则不收集任何分位数统计信息。
如果没有在 RUNSTATS 命令的列或表级别上指定 num_freqvalues 和 num_quantiles,那么 num_freqvalues 的值将从 num_freqvalues 数据库配置参数中获取,而 num_quantiles 的值将从 num_quantiles 数据库配置参数中获取。
可以为单个列或一组列修改频率和分位数统计信息的精确度。提高分布统计信息的精确度将导致更大的 CPU 和内存消耗,并占用更多的目录空间。对于这些分布统计信息,只考虑对拥有选择谓词的最重要的查询而言最为重要的列。
当出现下列任何一种条件时,RUNSTATS 将不收集分布统计信息:
列组(Column Group)统计信息将获得一组列的不同值组合的数目。通常,DB2 优化器可用的基本统计信息不检测数据相关性。列组的使用将给多个谓词的联合选择提供更准确的估计。列组统计信息假设数据是均匀分布的;但还无法获得列组上的分布统计信息。
较于列组的基数,单个列的基数(cardinality)的乘积将获得更好的相关性估计。
当在 RUNSTATS 中指定 LIKE STATISTICS 子句时,将收集附加的列统计信息。这些统计信息存储在 SYSIBM.SYSCOLUMNS 表里的 SUB_COUNT 和 SUB_DELIM_LENGTH 列中。它们仅针对字符串列进行收集,查询优化器用它们来提高“column LIKE '%abc'”和“column LIKE '%abc%'”类型谓词的选择性估计。
现在,可以在 DB2 V8.2 中为 RUNSTATS 建立一个统计信息的配置文件。统计信息配置文件是指一组选项,它预先定义了特定表上将要收集的统计信息。
当将命令参数“SET PROFILE”添加到 RUNSTATS 命令时,将在表描述符和系统目录中注册或存储统计信息配置文件。若要更新该统计信息配置文件,则可以使用命令参数“UPDATE PROFILE”。
没有删除配置文件的选项。
随着数据库快速不断地增长,通过访问所有数据收集统计信息的能力可能会受到固定的批量窗口、内存和 CPU 约束的阻碍。
目前,要运行表上的 RUNSTATS,就要执行全表扫描。通过数据抽样,只需扫描数据的一个子集即可。
如果一个查询试图预计总的趋势和模式,且某一误差域(margin of error)内的近似答案足以监测这些趋势和模式,那么数据抽样或许是比全表扫描更好的选择。
在 DB2 V8.1 中,引入了 SAMPLED DETAILED 子句,允许通过抽样计算详细的索引统计信息。该子句的使用将减少为获得详细索引统计信息而执行的后台计算量和所需的时间,但在大多数情况下,都会使数据足够的精确。
行级贝努里(Bernoulli)抽样利用 sargable(search + argument-able 谓词是一个可以由数据管理器来评估的谓词)谓词获得百分之 P 的表行样本,在样本中包含每一行的概率是 P/100,而不包含它的概率则是 1 - P/100。
例如,对于 10% 贝努里(Bernoulli)抽样,将会选择 10%(10/100)的行,而拒绝 90%(1-10/100)的行。在贝努里(Bernoulli)抽样中,每一页将会引发一次 I/O,因为要扫描该表。将生成一个随机数来确定是否选择一行(类似于以 P/100 的概率扔钱币)。即使每一页都发生一次 I/O,我们仍然节省了处理数据所需的 CPU 时间。
在行级贝努里(Bernoulli)抽样中,需要读取每一个数据页。然而,它仍然可以带来极大的性能提高,因为 RUNSTATS 是 CPU 密集的。如果索引是可用的,那么就会改进抽样。如果数据是群集的,它还可以提供更准确的统计信息(所获得的样本更好地代表了整个表数据)。
系统页级抽样与行级抽样类似,除了抽样的对象是页面而不是行。以 P/100 的概率选择每一页,而以 1 - P/100 的概率拒绝页的选择。在所选中的每一页中,要选择所有的行。系统页级抽样优于全表扫描或贝努里(Bernoulli)抽样的地方是它节省了 I/O。
抽样页也是预取的,所以该方法将比行级贝努里(Bernoulli)抽样更快。与不进行抽样相比,页级抽样极大地提高了性能。
RUNSTATS repeatable 子句允许通过 RUNSTATS 语句生成相同的样本,只要表数据没有发生更改。为了指定该选项,用户还必须提供一个整数,以表示将用于生成样本的种子(seed)。通过使用相同的种子,可以生成相同的样本。
总之,统计信息的准确性取决于抽样率、数据倾斜(data skew)以及用于数据抽样的数据群集。
对于数据库中的所有表,收集统计信息的一个可供选择的方法就是发出一条 REORGCHK 命令
REORGCHK UPDATE STATISTICS ON TABLE ALL
虽然该方法看上去是一种在多个表上收集目录统计信息的快速方法,但是仅仅当 REORGCHK 可以在合理的时间内完成执行时,该方法才是可取的。
在表的 LOAD REPLACE 期间以及索引的创建期间,也可以进行目录统计信息的收集。
在 UDB V8.2 中,为了在 LOAD 期间调用目录统计信息的收集,必须将选项“STATISTICS USE PROFILE”添加到 LOAD 控制语句中。选项“STATISTICS YES”仍然有用,但它现在已是过时的语法。若在表的 LOAD 之后在同一表上执行 RUNSTATS,那么在 LOAD 期间发出“STATISTICS USE PROFILE”的目的就是为了减少正常运行过程中占用的时间。在执行 load 之前,就必须创建统计信息配置文件,它现在允许指定与 RUNSTATS 命令中相同的统计信息选项。
为了可以对一个表或索引收集统计信息,必须能够连接到包含该表和索引的数据库,并具有下列授权级别之一:
当对一个表运行 RUNSTATS 时,有两种用户访问选项:允许读访问和允许写访问。
在 RUNSTATS 命令中的参数 ALLOW READ ACCESS 指定计算统计信息时,其他用户可以只读地访问该表。
在 RUNSTATS 命令中的参数 ALLOW WRITE ACCESS 指定计算统计信息时,其他用户可以读取或写入该表。如果该表在任何时刻都必须是可用的,那么应该使用该子句。
当在一个分区数据库中发出 RUNSTATS 命令,并且一个表分区位于发出 RUNSTATS 的数据库分区中时,那么 RUNSTATS 将在该数据库分区上执行。如果表分区不在该数据库分区上,那么将请求发送给数据库分区组中持有该表分区的第一个数据库分区。然后,在该数据库分区上执行 RUNSTATS 命令。
RUNSTATS 不是在分区的数据库中并行运行的,但对一个分区确定信息,然后对所有分区推断出合适的估计值。有一个隐式的假设:每个表中的行是均匀分布在每个多分区数据库分区组中的所有分区上的。
下列 IBM 技术札记(technote)谈到了加载一个表时运行 RUNSTATS 可能碰到的问题。其解决方案就是确保在加载表之前,为该表定义一个分区键(partitioning key)。
IBM Technote # 1153232(2004-02-12):
“加载表之后,Runstats 将多分区实例上 syscat.tables 中的列 CARD 更新为零行。
摘要
如果加载某一给定表之后,其中一个分区的行数为零,并且将从该分区执行 runstats,那么该命令将更新 syscat. 表中的列 CARD,显示该表的行数为零。这只对多分区的实例有影响。
内容
runstats 的行为使其将使用运行它的分区上的数据来推测该表中的行数。例如,如果加载之后所有数据都在一个分区上,并从另一分区执行 runstats,那么该命令将更新 syscat.tables 来表示该表的行数为零。然而,如果是从保存所有数据的分区运行 runstats,那么它将更新 syscat.tables 来展示该表包含((该分区上的行数)x(分区数目))行。
为了避免 runstats 的行为默认行为,需要在加载表之前,为该表定义分区键(partitioning key)。分区键需要位于高度惟一的一列或一组列上,从而确保数据均匀分布在该表所定义的所有分区上。”
stat_heap_sz 或统计信息堆大小的数据库配置参数指定了使用 RUNSTATS 命令收集统计信息中所用堆的最大尺寸。它是在启动 RUNSTATS 实用程序时分配的,然后当它完成时释放。stat_heap_sz 是代理私有内存的一部分。因此,在收集分布统计信息时,最好增大 stat_heap_sz 参数,以便能将更多的列放入这个堆中。处理较宽的表也需要更多的内存。当执行包含 SAMPLED DETAILED 选项的 RUNSTATS 时,必须额外分配 2 MB 内存,以确保 RUNSTATS 能够成功运行。
您可以使用下列策略来帮助减小 RUNSTATS 对于系统的性能影响:
仅在系统活动量少的时候安排执行 RUNSTATS,这是最大程度地减少系统影响的一个好方法。然而,对于一个 24 x 7 的系统,系统中可能没有可用的窗口或活动量少的时候。处理该情形的一种方法就是使用 RUNSTATS 的 throttling 选项。
throttling 选项将根据当前的数据库活动级别,来限制实用程序所占有的资源数量。UDB 中,在调整时,util_impact_lim 与 UTIL_IMPACT_PRIORITY 参数的交互确定了 RUNSTATS 的行为。UTIL_IMPACT_PRIORITY 关键字被用于诸如 RUNSTATS 的实用程序命令的子句中,而 util_impact_lim 则是一个实例配置参数。
util_impact_lim 参数是指允许所有已调整实用程序对于实例的工作负载产生影响的百分比。如果 util_impact_lim 是 100(默认值),则不用调整诸如 RUNSTATS 之类的实用程序调用。例如,如果将 util_impact_lim 设置为 10,那么已调整的 RUNSTATS 调用就被限定在消耗 10% 以下的工作负载。
UTIL_IMPACT_PRIORITY 关键字可充当一个开关,它指定 RUNSTATS 是否订阅调整策略。
可以在已声明的全局临时表(DGTT)上发出 RUNSTATS 命令,但是结果的统计信息不存储在系统目录中,因为声明的临时表条目没有目录条目。这些统计信息存储在表示声明的临时表的目录信息的内存结构中。如果整个查询经过时间中所节省的时间比完成 RUNSTATS 所增加的时间大得多,就建议发出 RUNSTATS。
下列 IBM 技术札记(technote)讨论了该问题:
IBM Technote # 1153051
“既不能从 SQL 例程(过程、UDF、方法)、SQL 动态复合语句发出 RUNSTATS 命令,也不能使用从 SQL 例程中发出的操作系统调用来发出该命令。如果需要发出 RUNSTATS 命令,那么可以直接从命令行处理器(Command Line Processor,CLP)发出它,或者通过使用 db2Runstats() API 函数(DB2 8.1 开始)从客户机应用程序或外部语言例程发出它,以及通过 LOAD 和 CREATE INDEX 语句发出该命令。”
可以创建一个名为“runstats”的 C 存储过程,调用 db2Runstats() API 函数。然后,可以使用 CALL 命令调用这个 C 存储过程。
下列 IBM 技术札记回答了该问题:
IBM Technote # 1023788:
“DB2 管理 API(编程接口)sqlustat 可以用于从 DB2 应用程序发出 RUNSTATS,例如写入调用级接口的应用程序。DB2 Administrative API Reference 手册中描述了 sqlustat。”
DB2 自动统计信息收集是在 DB2 UDB Version 8.2 中引入的。自动统计信息收集是完全自动表维护解决方案的一部分。其目标是允许 DB2 确定工作负载需要哪些统计信息,并定期在后台自动运行 RUNSTATS 实用程序,以便按需更新统计信息。
为了设置 SAMPLE 数据库自动进行统计信息收集,需要为自动维护开关设置数据库配置参数,如下所示:
|
下图(只显示统计信息收集和配置选项)展示了统计信息收集和配置的自动维护命令的层次结构和相关性。在该结构中,可以在最高层次快速关闭自动维护参数 AUTO_MAINT,而不丢失较低层的配置设置,比如 AUTO_RUNSTATS。
如果需要自动统计信息配置,那么可以打开参数 AUTO_STATS_PROF 和 AUTO_PROF_UPD。自动统计信息配置则通过确定何时和如何收集统计信息更进了一步。统计信息配置文件是自动生成的,自动统计信息收集过程将用它来调度 RUNSTATS。可以用内部算法来比较新收集的统计信息与已保存的一组统计信息,并基于某些触发条件发出包含抽样的 RUNSTATS。
当启用自动统计信息配置时,数据库活动的有关信息被收集并存储在查询反馈仓库中。然后,基于查询反馈仓库中的数据生成统计配置文件。
为了允许自动生成统计信息配置文件,需要设置两个数据库配置参数:
打开该参数将启动查询反馈数据的收集。
打开该参数,指定使用分析查询反馈数据的 DB2 中的建议来更新 RUNSTATS 配置文件。
在触发自动统计信息配置之前,必须通过运行 SYSINSTALLOBJECTS 存储过程创建查询反馈仓库。
按照下列方式调用该存储过程:
|
例如,要创建反馈仓库,需要运行下列存储过程:
|
该过程将创建下列表、存储建议以及与查询执行过程中碰到的谓词有关的信息:
当禁用 AUTO_PROF_UPD 参数时,可以将建议存储在 SYSTOOLS.OPT_FEEDBACK_RANKING 表中。然后,当手动更新 RUNSTATS 配置文件时,就可以查看该表中所存储的建议。
DB2 自动统计信息收集和配置生成只能在 DB2 串行模式下启用;它们在联邦的、SMP 和 MPP 环境中是不可用的