在检查后台alert文件时发现了JOB调用DBMS_STAT包的报错信息。详细的错误信息为:

Errors in file /opt/oracle/admin/tradedb/bdump/tradedb1_j000_10429.trc:

ORA-12012: 自动执行作业68 出错

ORA-20000: Insufficientprivileges to analyze an object in Schema

ORA-06512: 在 "SYS.DBMS_STATS", line 13323

ORA-06512: 在 "SYS.DBMS_STATS", line 13682

ORA-06512: 在 "SYS.DBMS_STATS", line 13760

ORA-06512: 在 "SYS.DBMS_STATS", line 13719

ORA-06512: 在 line 1

首先观察错误信息,错误信息采用堆栈的结构,第一个错误是调用最外面一层PL/SQL时的报错信息,随后的错误一层层深入下去,直到最后一行错误指出真正错误发生的位置。

从这个错误信息中能看出很多的东西。首先错误生成了一个文件,从文件的名称上(tradedb1_j000)10429.trc)就可以看出,这是JOB在后台运行时出现的错误,因为生成后台trace文件包含了j000进程信息。随后第一个错误也说明了这一点,指出JOB号68的错误在运行时发生了错误。

继续看第二个错误即ORA-20000,熟悉Oracle错误编号的人,就知道这个错误是PL/SQL自定义的错误。为了方便用户定制自己的错误和异常信息,Oracle将ORA-20000到ORA-20999之间的错误预留给用户,用户可以使用RAISE_APPLICATION_ERROR来生成自定义的错误信息。

接着看这个PL/SQL用户自定义信息的内容:Insufficient privilegesto analyze an object in Schema。这个信息很明确,在分析SCHEMA的对象时缺少权限。

随后的5个错误信息都是指出这个错误发生在DBMS_STATS包中的具体位置。

现在总结一下,看看我们已经了解了哪些情况:首先出现错误的一个后台JOB进程的ID是68,而导致错误的原因是由于缺少权限,使得调用DBMS_STATS包的时候出现了错误。

显然现在应该检查68号JOB,看看这个JOB调用的具体过程是什么:

SQL> SELECT JOB, WHATFROM DBA_JOBS WHERE JOB = 68;
JOB WHAT
---------- -------------------------------------------------------------------
68 DBMS_STATS.GATHER_SCHEMA_STATS(USER, METHOD_OPT=> 'FOR ALL INDEXED COLUMNS');

可以看到,这个JOB调用的过程是DBMS_STATS.GATHER_SCHEMA_STATS,也就是说用户在收集自己的统计信息。结合刚才的报错信息,发现问题有点奇怪。如果在收集其他用户的统计信息时出错是比较正常的,而收集自己的统计信息报权限问题,肯定不正常。

收集统计信息的过程要查询当前用户对象的源数据,要访问当前用户的表、索引,而这些操作对于对象的OWNER来说,不会存在任何的权限问题。

SQL> SELECT JOB, LAST_DATE,NEXT_DATE FROM DBA_JOBS WHERE JOB = 68;
JOB LAST_DATE NEXT_DATE
---------- ------------------- -------------------
68 2007-12-14 04:00:00 2007-12-21 12:37:27

检查一下JOB的其他信息,可以发现这个JOB在7天前还顺利执行过。那么现在报错很可能是和最近的数据库修改有关。

根据目前的情况判断,存在两种可能,一种是Bug造成的,另一种是由于最近的修改导致的。

退一步讲,即使是Bug造成了这个问题,那么多半也是最近的修改导致了Bug的发生,因为这个JOB在7天前还能够顺利执行。

如果是最近的修改导致了错误的发生,那么又是什么情况才会导致用户收集自己的统计信息出现权限不足呢。如果是用户下的表,则用户肯定有权限分析,如果表在其他用户下,SCHEMA的收集方式又不会去收集这个表的信息。看来问题应该不是出在表上。

再次观察JOB执行的命令:DBMS_STATS.GATHER_SCHEMA_STATS(USER,METHOD_OPT => 'FOR ALL INDEXED COLUMNS'),也就是说调用GATHER_SCHEMA_STATS过程的时候,仅指定了OWNNAME参数和METHOD_OPT参数,其他参数都采用的默认参数。

而根据DBMS_STATS.GATHER_SCHEMA_STATS过程参数的定义,CASCADE参数的默认值为:to_cascade_type(get_param('CASCADE'))。

通过SQL检查GET_PARAM(‘CASCADE’)的结果:

SQL> SELECT DBMS_STATS.GET_PARAM('CASCADE')FROM DUAL;
DBMS_STATS.GET_PARAM('CASCADE')
-----------------------------------------------------------
DBMS_STATS.AUTO_CASCADE

也就是说,默认情况下,DBMS_STATS.GATHER_SCHEMA_STATS调用采用AUTO_CASCADE作为CASCADE参数的值,而这个参数值的含义是由Oracle来决定是否收集索引的统计信息。至少说明,在收集用户统计信息的时候,很可能也会收集表索引的统计信息。那么会不会是索引造成的问题呢。表虽然不会跑到其他用户下,可是索引是可能建立到其他用户下的。

查询这个用户下所有表的索引的OWNER,果然发现问题:

SQL> SELECT DISTINCTOWNER FROM ALL_INDEXES WHERE TABLE_OWNER = 'GPO';
OWNER
------------------------------
SYS
GPO

进一步查询具体错误对象:

SQL> SELECT OWNER, TABLE_NAME, INDEX_NAME

2 FROMALL_INDEXES

3 WHEREOWNER = 'SYS'

4 ANDTABLE_OWNER = 'GPO';

OWNER TABLE_NAME INDEX_NAME
-------------------- ------------------------------ ------------------------------
SYS GPO_ACCREDIT_PRICE IND_GPO_ACC_PRICE_SELLER_ID

显然是某个DBA在建立索引时,没有切换用户所导致的问题。

查看索引的创建时间,果然是前一天才建立的,这就能说明为什么7天前JOB还可以顺利执行,而在今天运行时出现了错误。

SQL> SELECT OBJECT_NAME, CREATEDFROM USER_OBJECTS
2 WHEREOBJECT_NAME = 'IND_GPO_ACC_PRICE_SELLER_ID';
OBJECT_NAME CREATED
------------------------------ -------------------
IND_GPO_ACC_PRICE_SELLER_ID 2007-12-20 16:05:35

一旦找到了原因,问题的解决就很容易了。删除索引后,在GPO用户下重建,问题解决。

文章来源:《Oracle DBA手记1》第15章 执行计划与统计信息案例 作者:杨廷琨

配图来源:http://cdn2.business2community.com/wp-content/uploads/2014/06/solutions.jpg