背景:
某周六早上,某应用在5点52分通过sqlloader进行导数,采用先truncate后load数,6点14分导完7千多万数据,而6点03分,数据库自动统计信息收集作业,收集到该表的数据量为0,期间刚好有联机交易进来,导致执行计划发生变化。而在导数后,程序尚未手工收集统计信息,最终导致故障发生。后面复盘时,发现在10点08分,数据库自动统计信息收集作业,又收集了一次该表的数据,这次的数据量是7千多万。详细案例复盘,可以参考我们内网“秘技阁”的数据库案例文章。
到此,你可能会有疑问,周六、日,数据库的收集窗口很长,为啥6点14分已导完7千多万数据,等到10点08分才被数据库自动收集?延迟这么久?
先科普以下两点信息:
从oracle 11g开始,数据库默认的统计收集窗口为:
。周一至周五,每晚22点至第二天凌晨2点,共4小时
。周六、周日,从早上6点,持续20小时
表被自动收集统计信息的前提条件(自上次统计信息以来):
。表的inserts + deletes + updates超过表的总记录数的10%
。表被truncate过
为了弄清楚根源问题,周六在测试环境模拟了一下场景:
1)使用CTAS方式,创建TMP_211218表, 表的数据量为74434,在16点左右,由于需要测试另一个功能,我对该表进行了truncate,后面又重新将dba_objects的数据插回TMP_211218表,最终数据量不变。 大家重点关注该表在18点之前的操作。

2)查看数据库自动统计信息收集的作业日志,大家可以看,从周六早上6点创建第1个作业后,每隔4个小时,数据库会自动1个作业,18点05分那个作业,运行了16秒就结束

3)在18点15分08秒时,对表TMP_211218进行truncate。从下图可以看到删除记录数及最后操作时间

至少,细心的朋友可能已经看出了问题了。
等待总是那么的煎熬,晚上我卡点又远程上去瞧了一下。
4)22点04分39秒,数据库尚未创建22点那个统计信息收集作业,dba_tab_modifitions还一直记录着下午18点15分08秒那次truncate。

5)再等了一分钟,再次查询,发现22点那个作业已经运行完毕,dba_tab_modifitions表已经没有记录了

6)查看表的统计信息,可以看到最后收集时间为22点05分

7)查看表的历史统计信息收集,该表不记录最后一次收集,从红框中看到收集时间是16点00分34秒,不在前面提到的常规收集每4个小时自动创建的统计信息收集作业窗口时间内。16点左右,我短暂调过收集参数,验证了功能后,又关掉了参数。看下图作业名为ORA$_ATSK_AUTOSMT,好几个都是在16点左右那几分钟

8)通过表的历史快照,也可以看到,18点15分08秒truncate表,删了7万多条记录,19、20、21、22这几次整点的快照,都录到了当时数据库变化情况,但没被自动收集,直到22点05分那次自动统计信息收集作业,才收集了该表的准确信息,隔了近4个小时

总结:
从上面的测试可以看到,虽然周六、日整个维护窗口有20个小时,每隔4个小时会创建一个收集作业,如果当前业务表满足了被自动收集的触发条件,但当前收集作业已结束,那么只能等下一个收集作业调起。因此,应用开发人员需要清楚了解自己的数据特点,在做大批量的导数或清数后,需要手工及时去收集统计信息,避免由于统计信息不准确,而影响了SQL的稳定性。不能太依赖于数据库本身的自动收集作业。
当然,在oracle 19c引入了high-frequency automatic optimizer statistics collection功能,可以解决以上问题,让收集的频率更短一些,但也存在一些缺陷,及频率的合理定义。请参考附录二:high-frequency automatic optimizer statistics collection
附录一:16点,重新插入记录截图

附录二:high-frequency automatic optimizer statistics collection




