在报表数据库的后台alert文件中发现了这个错误简单记录一下问题的诊断和解决过程数据库版本 for Solaris sparc 寻找产生问题的真正原因 在第一篇文章中定位了问题并且找到了解决方法;在第二篇文章中找到了导致源数据库和目标数据库执行计划不同的原因 但是到目前为止还没有找到这个问题产生的真正原因 首先理一下思路根据第一篇文章的描述产生ORA问题的原因是由于一个大数据量的插入语句选择了一个十分糟糕的执行计划而导致Oracle选择了这个执行计划的直接原因是由于列的统计信息出现了错误而在第二篇文章中可以确认由于源数据库的版本为没有使用列统计信息中的DENSITY列所以没有引发这个问题而在目标数据库版本为Oracle使用了统计信息列DENSITY的值所以Oracle认为访问ORD_HIT_COMM表且通过ENABLE_FLAG列进行限制只会返回条记录这就导致了Oracle产生了一个错误的离谱的执行计划 现在的问题是什么导致了源数据库错误统计信息的产生 这就需要检查源数据库数据和统计的来源因为在源数据库上直接收集统计信息是不会得到这种DENSITY的 经过检查发现这个的源数据库仍然不是数据的真正源头而真正的来源数据库版本是 发现了这个信息那么问题的产生就不奇怪了 看一下上这张表的统计信息 SQL>SELECTCOLUMN_NAMENUM_DISTINCTNUM_NULLSDENSITYNUM_BUCKETSHISTOGRAM FROMUSER_TAB_COLUMNS WHERETABLE_NAME=ORD_HIT_COMM ANDCOLUMN_NAME=ENABLE_FLAG; COLUMN_NAMENUM_DISTINCTNUM_NULLSDENSITYNUM_BUCKETSHISTOGRAM ENABLE_FLAGEFREQUENCY 可以看到在Oracleg使用了BUCKETS的设置而且USER_TAB_COLUMNS添加了一个字段HISTOGRAM用来表示列的统计信息的类型 FREQUENCY类型和以往的HEIGHT BALANCED类似的列统计不同使用FREQUENCY类型Oracle会选择与NUM_DISTINCT相同数量的NUM_BUCKETS来进行直方图统计而直方图统计信息方式和基于高度的统计信息是不同的最关键的是这种统计方式的DENSITY的结果和HEIGHT BALANCED的计算方式大不相同 因此在g中由于Oracle了解当前列的统计信息方式为FREQUENCY类型因此可以根据直方图的信息得到正确的执行计划和返回记录数 SQL>SELECTCOLUMN_NAMENUM_DISTINCTNUM_NULLSDENSITYNUM_BUCKETSHISTOGRAM FROMUSER_TAB_COLUMNS WHERETABLE_NAME=ORD_HIT_COMM ANDCOLUMN_NAME=ENABLE_FLAG; COLUMN_NAMENUM_DISTINCTNUM_NULLSDENSITYNUM_BUCKETSHISTOGRAM ENABLE_FLAGEFREQUENCY rowselected SQL>EXPLAINPLANFOR SELECT*FROMORD_HIT_COMMWHEREENABLE_FLAG=; Explained SQL>SELECT*FROMTABLE(DBMS_XPLANDISPLAY); PLAN_TABLE_OUTPUT |Id|Operation|Name|Rows|Bytes|Cost(%CPU)| ||SELECTSTATEMENT||K|M|()| ||TABLEACCESSFULL|ORD_HIT_COMM|K|M|()| rowsselected 但是如果将统计信息导入到数据库中就会存在严重的问题由于i的数据库中没有表示统计信息类型的HISTOGRAM列因此即使是基于FREQUENCY类型的统计信息也会被当作基于HEIGHT BALANCED类型的统计信息 而且从导入的统计信息可以看到虽然直方图的统计信息被导入但是USER_TAB_COLUMNS中的NUM_BUCKETS列的值为也就是说中优化器根本不会去考虑直方图信息而是直接通过NUM_DISTINCT和DENSITY的值来确定执行计划和返回记录数 对于版本Oracle都使用NUM_DISTINCT的值也就避免了问题的产生而在中Oracle使用了DENSITY的值而这个值并不是版本的DBMS_STATS包生成的统计信息而是从g环境中导入的且这个值在g的FREQUENCY类型的统计信息中已经改变了计算方法使得计算结果比环境中要小得多从而导致了上错误执行计划的产生 显然整个问题完全是由于版本差异造成的这个问题说明在将g的表导入到环境中最好不要导入统计信息 在导出阶段或在导入阶段设置STATISTICS = NONE避免g的统计信息导入到环境中在导入过程结束后手工在环境上重新收集统计信息 一旦g的统计信息被导入到环境中就必须重新收集统计信息 SQL>SELECT*FROMV$VERSION; BANNER OracleiEnterpriseEditionReleasebitProductionPL/SQLReleaseProduction COREProduction TNSforLinux:VersionProduction NLSRTLVersionProduction SQL>SELECTCOLUMN_NAMENUM_DISTINCTNUM_NULLSDENSITYNUM_BUCKETS FROMUSER_TAB_COLUMNS WHERETABLE_NAME=ORD_HIT_COMM ANDCOLUMN_NAME=ENABLE_FLAG; COLUMN_NAMENUM_DISTINCTNUM_NULLSDENSITYNUM_BUCKETS ENABLE_FLAGE SQL>EXPLAINPLANFOR SELECT*FROMORD_HIT_COMMWHEREENABLE_FLAG=; 已解释 SQL>SELECT*FROMTABLE(DBMS_XPLANDISPLAY); PLAN_TABLE_OUTPUT |Id|Operation|Name|Rows|Bytes|Cost| ||SELECTSTATEMENT||||| |*|TABLEACCESSFULL|ORD_HIT_COMM|||| PredicateInformation(identifiedbyoperationid): filter(ORD_HIT_COMMENABLE_FLAG=) Note:cpucostingisoff 已选择行 这时g的统计信息已经导入到环境中如果忘记重新收集统计信息就会导致这个错误的产生 SQL>EXECDBMS_STATSGATHER_TABLE_STATS(USERORD_HIT_COMM) PL/SQL 过程已成功完成 SQL>SELECTCOLUMN_NAMENUM_DISTINCTNUM_NULLSDENSITYNUM_BUCKETS FROMUSER_TAB_COLUMNS WHERETABLE_NAME=ORD_HIT_COMM ANDCOLUMN_NAME=ENABLE_FLAG; COLUMN_NAMENUM_DISTINCTNUM_NULLSDENSITYNUM_BUCKETS ENABLE_FLAG SQL>EXPLAINPLANFOR SELECT*FROMORD_HIT_COMMWHEREENABLE_FLAG=; 已解释 SQL>SELECT*FROMTABLE(DBMS_XPLANDISPLAY); PLAN_TABLE_OUTPUT |Id|Operation|Name|Rows|Bytes|Cost| ||SELECTSTATEMENT||K|M|| |*|TABLEACCESSFULL|ORD_HIT_COMM|K|M|| PredicateInformation(identifiedbyoperationid): filter(ORD_HIT_COMMENABLE_FLAG=) Note:cpucostingisoff 已选择行 这个问题也从另一个角度说明进行跨版本迁移测试工作的重要性 |