我们通过学习知道,rowid 是存放行在磁盘上的具体位置,格式如下 AAAVU/AAHAAAACVAAA。

本章内容我们来揭秘下 rowid。

通过 rowid 去寻找数据是最快的,因为我都把物理位置告诉 oracle 了,oracle 只需要一次 IO 就能找到该数据。

那么 rowid 在块中是真实存储的吗?

显然不是,进程要读取哪个对象,哪个文件,哪个 BLOCK,哪一行时,这几个信息就动态拼接了 ROWID 并展现。所以没必要存储,浪费资源空间。

rowid 的组成

通过以下 SQL 我们把 rowid 显示为人能看懂的信息,那么我们能不能自己通过计算得出来呢,可以的,但我们要先了解下 rowid 的构成。

SELECT 
  DBMS_ROWID.ROWID_OBJECT('AAAVU/AAHAAAACVAAA') AS object_id,
  DBMS_ROWID.ROWID_RELATIVE_FNO('AAAVU/AAHAAAACVAAA') AS file_id,
  DBMS_ROWID.ROWID_BLOCK_NUMBER('AAAVU/AAHAAAACVAAA') AS block_number,
  DBMS_ROWID.ROWID_ROW_NUMBER('AAAVU/AAHAAAACVAAA') AS row_number
FROM dual;

|OBJECT_ID|FILE_ID|BLOCK_NUMBER|ROW_NUMBER|
|---------|-------|------------|----------|
|87,359   |7      |149         |0         |

rowid 使用 base64 编码行的物理地址,编码字符包含 A-Z,a-z,0-9,+和 /。

rowid 由四部分组成,共 18 位代表 80 位二进制数,占用 10 个字节。

32bit obj# + 10bit file# + 22bit block# + 16bit row#

其中obj 用 6 位字符显示,file 用 3 位字符显示,block 用 6 位字符显示,row 用 3 位字符显示

所以,上诉例子可以分解为以下:

obj

file

block

row

AAAVU/

AAH

AAAACV

AAA

参考 base64 的编码规则,我们把 rowid 转换一下

AAAVU/ = A(0) + A(0) + A(0) + V(21) + U(20) + /(63)

AAH = A(0) + A(0) + H(7)

AAAACV = A(0) + A(0) + A(0) + A(0) + C(2) + V(21)

AAA = A(0) + A(0) + A(0)

得到转换后的 base64 后怎么计算呢。其实很简单。通过 从右到左 按 64 的幂次方计算就行了,

第 1 位(最右):×64⁰ ,第 2 位:×64¹ ,第 3 位:×64²以此类推。那我们代入公式自己计算一下对不对。

AAAVU/ = A(0) + A(0) + A(0) + V(21) + U(20) + /(63)=21*642+20*641+63*640 = 63 + 1280 + 86016 = 87359

AAH = A(0) + A(0) + H(7) = 7*640 =7

AAAACV = A(0) + A(0) + A(0) + A(0) + C(2) + V(21) =2*641+21*640 =128+21=149

AAA = A(0) + A(0) + A(0) = 0

跟我们用 SQL 得出的结果是一致的。

为什么一个数据文件最大是 32G,我们知道,数据文件 = 块个数 * 块大小。

那这个块个数是怎么得出来的。我们上面有说到,block 是占用22bit,也就

块编号的最大值 = 

222−1=4,194,303222−1=4,194,303 或者 SELECT POWER(2, 22) max_block FROM dual;

数据文件的最大值

select 4194304*8/1024/1024 from dual;得出 32G