李真旭(Roger)
ACOUG 核心专家,Oracle ACE,云和恩墨技术专家
编辑手记:由一个问题入手,深入其根本原因,层层递进,细致分析,由点及面,这是DBA进阶的捷径之一。
某客户说一套数据库由于非正常关机重启之后,进行数据导出发现报错,expdp 无法正常工作,报错之后直接退出:
而检查此时的 alert log 可以发现有如下类似的错误:
从上面的信息我们可以得到如下几个结论:
1、expdp 的写进程报错,因为日志产生是 dw 进程。
2、dw进程报错的原因是遭遇了 ora-07445 [klufprd()+321] 错误。
3、对于 [klufprd()+321] 这个函数,非常少见,但是从前面2点我们可以知道这肯定与 buffer cache 有关系。
所以要临时解决这个问题也很简单。通过 alter system flush buffer_cache 刷新缓存之后,再次进行 expdp 操作。
后续客户尝试了之后,发现 expdp 操作虽然仍然会报错,但是 expdp 不会异常终止,会继续完成后面其他对象的导出。
进一步分析报错的信息可以看到,有如下这样的提示:
很明显,这里提到这个这个表,恰好就是 expdp 报错所遇到的表,只不过我们刷新 buffer cache 之后,expdp 可以跳过这个表继续完成其他对象的导出。
从上述的信息来看,这里存在错误。客户也意识到,通过 dbv 对数据文件进行检查,但是发现文件并没有损坏。
这里我们要注意,dbv 同时是检查物理坏块,对于逻辑坏块通常无能为力,当然块内的逻辑错误,这类型的块 dbv 是可以检查出来的。
但是从这里的信息来看,Oracle 发现所需要的这行记录 row 01808438.0 应该在 file 6 block 33784 中找到,但是却并没有发现。
注意,这里的 file 6 block 33784 本身是完好的。
那么这里的 row 01808438.0 表示什么含义呢?
其实这是表示的 nrid;这可以理解为一直指针;其中前面一部分是表现 rdba 地址,后面表现行编号。
如果要进一步分析为什么会这个错误,我们怎么办呢? 很简单,分别将 block 33784以及rdba 01808438(16进制) 进行 dump。 如下是转换的脚本:
日志报错中提到的是 row 01808438.0 ,那么我们首先来分析 file 6 block 33848的dump:
上述类似表示的是 rdba 地址 01808438 的第0行,也就是我们大家所理解的第一行。我们可以发现这行记录中,行头存在一个nrid地址。
说到 nrid 地址,这通常是针对行链接,行迁移才会遇到的一种情况。那么这里为什么会出现呢?
行迁移几种,最常见的一种其实是 block 内的。一个 block 中单条记录的最大列数是255列,当一行记录的列超过255时,其他的列数据库会被 oracle 分成另外一个 row piece 存在同一个 block 中(当然也有可能存到其他 block)。
也就是说超过255列的行数据,会被分成多个 row piece;而当我们读取这个行数据时,怎么知道是一个完整的整体呢?
答案就是 nrid,oracle 通过 nrid 来将这多个 row piece 串在一起,组成一个完整的行数据。
想到这一点,那么我们再回头去看下前面的错误。row 01808438.0 表示这个 block 的第0行,而该 block 的第0行所存在的 nrid 地址是:0x018083f8.e
那么我们进一步到 block 0x018083f8 中去寻找第e行记录,发现结果是这样的:
上述类似表示的是 rdba 地址01808438的第0行,也就是我们大家所理解的第一行。我们可以发现这行记录中,行头存在一个 nrid 地址。
说到 nrid 地址,这通常是针对行链接,行迁移才会遇到的一种情况。那么这里为什么会出现呢?
行迁移几种,最常见的一种其实是 block 内的。一个 block 中单条记录的最大列数是255列,当一行记录的列超过255时,其他的列数据库会被oracle 分成另外一个 row piece 存在同一个 block 中(当然也有可能存到其他 block)。
也就是说超过255列的行数据,会被分成多个 row piece;而当我们读取这个行数据时,怎么知道是一个完整的整体呢?
答案就是 nrid,oracle 通过nrid来将这多个 row piece 串在一起,组成一个完整的行数据。
想到这一点,那么我们再回头去看下前面的错误。row 01808438.0 表示这个 block 的第0行,而该 block 的第0行所存在的 nrid 地址是:0x018083f8.e
那么我们进一步到 block 0x018083f8 中去寻找第e行记录,发现结果是这样的:
我们可以看到这里对应的记录根本就没有。因为该 block 最后一条记录是 row 13,也就是第14行,也是一个 row piece,而且存在一个 nrid.
该 nrid 是 0x018083f8.c,这表示该 block 33784 第12行记录。跟 row 13 是组合成一条完整行记录的。
换句话说,我们前面报错的那条记录,应该有2个 row piece,其中一个 row piece 是存在的,其中一个 row piece 本应该存在在 33784 block 中。
但是由于找不到该 row piece,因此 oracle 报了上述的错误。
实际上该错误遇到之后,我们通常以为是 index 的问题,通过 drop 重建可以解决,然而这里的问题比较特殊,据说是表的数据有问题。
所以这就是为什么客户重建 index 会报错的原因:
最后,我们清楚了所有原因,那么要解决该问题很简单。通过 rowid 的方式跳过这行有问题的记录,将其他数据取出,重建表即可。
联系客服