管理操作的日志记录
dbms_stats包中的许多过程在数据字典中记录关于它们的执行的信息。这些日志信息通过dba_optstat_operations以及在12.1多租户环境下的cdb_optstat_operations视图来予以展现。基本上,你可以查到执行了哪些操作,它们是什么时候开始执行的以及执行了多久。从12.1版本开始,关于状态、会话以及与操作相关的作业信息(可选)都可以访问。接下来的例子摘自一个生产数据库,显示了gather_database_stats过程每天都会启动并花费9-18分钟来执行(注意,2014年4月5、6日是周末):
①状态可能出现的值有:
PENDING、IN PROGRESS、COMPLETED、FAILED、SKIPPED以及TIMED OUT。
select operation,
start_time,(end_time-start_time) day(1) to second(0) as duration
from dba_optstat_operations
order by start_time desc;
p240
此外,从12.1版本开始,你可以查询到某个操作执行时使用的参数。例如,下面的查询展示了默认收集作业的最后一次执行都使用了哪些参数:
select x.*
FROM dba_optstat_operations o,
XMLTable('/params/param'
PASSING XMLType(notes)
COLUMNS name VARCHAR2(20) PATH
'@name',
value VARCHAR2(30) PATH
'@val') x
WHERE operation =
'gather_database_stats(auto)'
AND start_time =
(select max(start_time)
FROM dba_optstat_operations
WHERE operation = 'gather_database_stats(auto)');
要知道日志信息是使用与之前描述的统计信息历史相同的机制来清除的。因此,两者拥有相同的保留期。
保持对象统计信息为最新的策略
基本的原则,也可能是最重要的一条,就是查询优化器需要对象统计信息来描述存储在数据库中的数据。因此当数据变化时,对象统计信息也应该跟着变化。你可能也清楚,我是提倡定期收集对象统计信息的。那些反对这项实践的人会争论说,如果一个数据库运行良好,就没有必要重新收集对象统计信息。
这种方法的问题通常是,一些对象统计信息依赖于真实的数据。例如,有这样一种统计信息,其经常变化的是那些包含数据(比如与某个交易、某个销售或某个电话相关联的时间戳)的列的低/高值。诚然,在一般的表中它们的变化占少数,但是通常变化的那部分很关键,因为它们会在应用程序中被反复使用。在实践中,我遇到过的由于没有最新的对象统计信息而导致的问题比反过来的情况要多得多。
显而易见,在永远不变的数据上收集对象统计信息毫无意义。只有陈旧的对象统计信息应该被重新收集。因此,利用记录出现在每张表上的修改数量的特性就显得必不可少。用这种方法,你可以只对那些经历了大量修改的表重新收集统计信息。默认情况下,当一张表有超过10%的数据发生了变化就被认为是陈旧的。这是个合理的默认值。从11.1版本开始,如果有必要可以修改这个默认值。
收集对象统计信息的频率也是一个存在不同看法的问题。我见过各种成功的案例,有按小时的,有按月的,甚至有更低频率的。这其实依赖于你的数据。无论如何,当使用表的陈旧属性作为重新收集对象统计信息的基础时,太长的间隔会导致过量的陈旧对象出现,而太短的间隔又会导致统计信息收集需要过多的时间以及资源使用出现高峰。
为此,我喜欢将它们安排得更频繁(为了分散负载)并保持单一的运行时间尽可能地短。如果你的系统有每天或每周的低使用率时段,那么将收集安排在那些时间段通常是一个好主意。如果你的系统是一个真正的7×24系统,那通常更好的做法是尽可能使用非常频繁的调度(每天执行许多次)来分散负载并避开高峰期。
如果你有加载或修改大量数据的作业(例如,在数据仓库环境中的ETL作业),就不应该等候一个已安排的对象统计信息的收集完成。而要直接将要修改的对象的统计信息收集作为作业本身的一部分。换句话说,如果你知道某些事物发生了大量变化,应立即触发统计信息的收集。
如果出于某个原因,你觉得不应该在某些表上收集对象统计信息,那就锁定它们。这样的话,定期收集对象统计信息的作业就会直接跳过这些表。这比完全禁止整个数据库的作业活动要好得多。
应该尽可能多地利用默认的收集作业。要在这方面满足你的要求,你应该检查默认的配置,如果有必要则进行更改。因为在对象级别的配置仅从11.1版本开始才可用,如果在之前的版本中对某些表有特别的需求,应该在默认作业之前安排一个作业来处理它们。通过这种方式,只会处理拥有陈旧统计信息对象的默认作业,而会直接跳过已处理的表。锁定可能也会有助于确保仅特定作业才会在那些关键表上重新收集对象统计信息。
相反,如果你正在考虑完全禁止默认的收集作业,则应该为oracle设置autostats_target首选项。那样的话,就让数据库引擎处理好数据字典,而对于其他的表,可以设置一个具体的作业来完成你所期望的工作
如果收集的统计信息导致无效率的执行计划,那么你可以做两件事。第一是通过还原本次收集统计信息之前顺利使用的对象统计信息来修复问题。第二是找出为什么查询优化器使用新的对象统计信息会生成无效率的执行计划。为此,你首先应该检查最近收集的统计信息是否正确地描述了数据。
举例来说,有可能伴随着新的数据分布的采样会导致不同的直方图。如果对象统计信息不良,那么收集本身,或者收集使用的参数就是问题所在。如果实际上对象统计信息没有问题,那么还有两种可能的原因。要么是查询优化器没有正确配置,要么是查询优化器犯了错误。你几乎无法控制后者,但是应该能够为前者找到解决方案。无论如何,应该避免匆忙认定是收集对象统计信息的固有问题,而因此停止定期收集它们。
最佳实践是使用dbms_stats包收集对象统计信息。但是,确实存在正确的对象统计信息误导查询优化器的情况。一个常见的例子是历史数据必须保持在线很长时间(举个例子,在瑞士某些类型的数据必须至少保存十年)。
在这种情况下,如果数据分布几乎不随着时间发生改变,通过dbms_stats包收集的对象统计信息应该没有问题。与此相反,如果数据分布严重依赖于时间段而且应用程序经常只访问数据的一部分,那么就有理由手工修改(也就是,捏造)对象统计信息用以描述大部分相关数据。换句话说,如果你知道dbms_stats包忽略的或者无法发现的某些东西,那么就可以合理使用捏造的对象统计信息来告知查询优化器。