怎样计算出正确的checksum值

| No Comments

在"怎样在windows上用DD配合ultraEdit修改数据"一文中我曾经提到"请注意,我这次测试的结果是:一定要先把上述block16 17位清零,同时把15位改成0x04alert log计算出来的checksum值才是对的,如果你不这样做,可以从alert log看到oracle这里计算出来的checksum值明显不对"。

 

这个观点是不正确的!

 

正确的观点是只需要把block16 17位清零,那么从alert log就可以看到oracle计算出来的checksum值了,而且这个时候alert log里的checksum值就是这个blockoracle计算出来的正确的checksum值。

 

因为oraclealert log里记录的computed block checksum 值实际上是并不是这个blockoracle计算出来的正确的checksum值,而是这个blockoracle计算出来的正确的checksum值与这个block的第1617位记录的checksum值做异或操作后的值

 

当你把一个block1617位清零后,因为0与一个值做异或操作后还是等于那个值,所在在将1617位清零后,这个时候alert log里的checksum值就是这个blockoracle计算出来的正确的checksum值了。

 

好了,我们来看一个例子:

SQL> select file_name from dba_data_files where tablespace_name='TESTTBS';

 

FILE_NAME

--------------------------------------------------------------------------------

/dras20/astca/test01.dbf

 

SQL> create table t1 (id number, c1 char(2000), c2 char(2000), c3 char(2000)) tablespace testtbs;

 

Table created

 

SQL> insert into t1 VALUES (1, 'A', 'A', 'A');

 

1 row inserted

 

SQL> commit;

 

Commit complete

 

SQL> select id,dbms_rowid.rowid_relative_fno(rowid)||'_'||dbms_rowid.rowid_block_number(rowid) location from t1;

 

        ID LOCATION

---------- --------------------------------------------------------------------------------

         1 133_10

 

现在我们先shutdown上述数据库然后用dd133_10这个block给拷出来:

$ dd if=/dras20/astca/test01.dbf of=/dras20/astca/test01.dd count=1 skip=10 bs=8192

1+0 records in.

1+0 records out.

 

然后再把test01.dd ftp到本地,然后用ultraEditID1的那条记录的ID值由1改为2

可以看到这个block现在在块头记录的checksum值是F9 8F

00000000h: 06 02 00 00 21 40 00 0A AA 09 DB 2F 00 08 01 06

00000010h: F9 8F 00 00 01 00 00 00 00 01 50 87 AA 09 DB 2A

 

现在我们将将ID1的那条记录的ID值由1改为2,即将C1 02改成C1 03

00000870h: 00 00 00 00 00 00 00 00 00 00 00 00 00 2C 01 04

00000880h: 02 C1 03 FE 07 D0 41 20 20 20 20 20 20 20 20 20

 

改完后ftpAIX上,然后再用dd把上述修改过的block给拷回去,再startup上述数据库:

$ dd if=/dras20/astca/test01.dd of=/dras20/astca/test01.dbf bs=8192 seek=10 count=1 conv=notrunc

1+0 records in.

1+0 records out.

 

SQL> select * from t1;

 

select * from t1

 

ORA-01578: ORACLE data block corrupted (file # 133, block # 10)

ORA-01110: data file 133: '/dras20/astca/test01.dbf'

 

现在我们去看一下alert log

Corrupt block relative dba: 0x2140000a (file 133, block 10)

Bad check value found during buffer read

Data in bad block -

 type: 6 format: 2 rdba: 0x2140000a

 last change scn: 0x0008.aa09db2f seq: 0x1 flg: 0x06

 consistency value in tail: 0xdb2f0601

 check value in block header: 0xf98f, computed block checksum: 0x100

 spare1: 0x0, spare2: 0x0, spare3: 0x0

***

Reread of rdba: 0x2140000a (file 133, block 10) found same corrupted data

 

从上述alert log里我们可以看出以下两点:

1、块头记录的checksum值是F9 8Foracle这里做异或操作后的checksum值是01 00

2oracle当发现checksum值不对的时候会尝试再次读一下该block

 

好了,我们现在根据F9 8F01 00来计算出上述block在修改后正确的checksum

F9 8F = 1111 1001 1000 1111

01 00 = 0000 0001 0000 0000

 

根据异或算法原理,这里很容易可以看出oracle计算出来的正确的checksum值应该是:

1111 1000 1000 1111,即F8 8F

 

好了,我们这里如法炮制再改一次上述blockchecksum值,即将上述blockchecksum值改为F8 8F,然后再startup上述数据库:

SQL> select id,dbms_rowid.rowid_relative_fno(rowid)||'_'||dbms_rowid.rowid_block_number(rowid) location from t1;

 

        ID LOCATION

---------- --------------------------------------------------------------------------------

         2 133_10

 

看到了吗,这里我们的确是根据alert log正确的计算出了上述block在改动数据后的正确的checksum

Leave a comment