数据库-Oracle中聚簇表的使用

                       OracleCluster Table的使用


大家通常oracle中的cluster的理解是不准确的,经常和sql server中的cluster index混淆。Cluster是存储一组table的一种方法,这些table共享同一数据块中的某些相同column,并把不同table在这一共享column上值相同的data row存储到同一block上。在sql server中的cluster index强制行根据index key按存储顺序存储,这一点和oracle中的IOT类似。   


从下图中我们可以清楚的看到cluster 和非cluster 的一组table的物理存储的区别。在cluster中,单个block上的数据可能来自多个table,概念上可以存储预连接的数据。单个table也可以使用cluster,即根据某些column按组存储数据。如图中,所有部门ID20110的部门信息和员工信息的数据都将存储在同一block。注意,这里存储的并不是排序的数据(那是IOT的任务),存储的是按部门ID分组集合的数据,是以heap的方式存储的。因此,部门20刚好和部门110相邻,而部门99和部门100相距很远(硬盘的物理位置)。

 

当单个block放不下时,额外的block将链接到最初的block,来包容溢出的数据,这种方式和在IOT中溢出block非常相似。

现在我们看看如何创建一个cluster。在cluster中创建一系列table是很简单的,对象存储定义如PCTFREEPCTUSEDINITIAL都是和cluster相关的,而不是和table相关。这是因为在cluster中存储了若干table,每个table在同一个block中拥有不同的PCTFREE没有意义。


SQL> create cluster e_d_cluster

  2  (deptid number(2))

  3  size 1024

  4  /


Cluster created

这里首先创建了一个index cluster。这个clusterkeydeptid,在table中这个列可以不命名为deptid,但数据类型number2)必须匹配。Size选项是用来告诉oracle预计有1024

字节数据和每个cluser key相关。Oracle将使用这个信息来计算每个block能容纳的最大cluster key数目。因此size太高,在每一block将得到很少的key,并且将使用比需要的更多的空间;设置容量太低,将得到过多的数据连接,这将偏离使用cluster的目的。Sizecluster的重要参数。

现在我们来创建cluster index。在把数据放入之前,需要索引clusterCluster index的作用是存储一个cluster key,并且返回包含该keyblock的地址。


SQL> create index e_d_cluster_idx

  2  on cluster e_d_cluster

  3  /


Index created

Cluster key index可以使用index所有的正常的存储参数,并且可以位于另一个tablespace。它是一个正常的index,能够索引到一个cluster,并且也包含一个完全null的条目。

我们在cluster中创建table

SQL> create table department

  2  (deptid number(2) primary key,

  3  dname varchar2(14),

  4  loc varchar2(13))

  5  cluster e_d_cluster(deptid);


Table created


SQL> create table employee

  2  (empid number primary key,

  3  ename varchar2(10),

  4  job varchar2(10),

  5  mgr  number,

  6  hiredate date,

  7  sal number,

  8  comm number,

  9  deptid number(2) references department(deptid))

 10  cluster e_d_cluster(deptid);


Table created


这里创建table与普通的table唯一的区别就是使用了cluster关键字,我们往table中装载数据:

SQL> begin

  2    for x in(select * from scott.dept)

  3    loop

  4      insert into department

  5      values(x.deptno,x.dname,x.loc);

  6      insert into employee

  7      select * from scott.emp

  8      where deptno = x.deptno;

  9    end loop;

 10  end;

 11  /


PL/SQL procedure successfully completed

 我们可以看到目前table中装载了如下数据:

SQL> select * from department;


DEPTID DNAME          LOC

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

    10 ACCOUNTING     NEW YORK

    20 RESEARCH       DALLAS

    30 SALES          CHICAGO

40 OPERATIONS     BOSTON

   

SQL> select * from employee;


     EMPID ENAME      JOB            MGR HIREDATE           SAL     COMM  DEPTID

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

      7782 CLARK      MANAGER        7839 1981-6-9          2450                10

      7839 KING       PRESIDENT             1981-11-17        5000                10

      7934 MILLER     CLERK            7782 1982-1-23         1300                10

      7369 SMITH      CLERK            7902 1980-12-17         800                20

      7566 JONES      MANAGER          7839 1981-4-2          2975                20

      7788 SCOTT      ANALYST          7566 1982-12-9         3000                20

      7876 ADAMS      CLERK            7788 1983-1-12         1100                20

      7902 FORD       ANALYST          7566 1981-12-3         3000                20

      7499 ALLEN      SALESMAN         7698 1981-2-20         1600        300     30

      7521 WARD       SALESMAN         7698 1981-2-22         1250        500     30

      7654 MARTIN     SALESMAN         7698 1981-9-28         1250       1400     30

      7698 BLAKE      MANAGER          7839 1981-5-1          2850                30

      7844 TURNER     SALESMAN         7698 1981-9-8          1500          0     30

      7900 JAMES      CLERK            7698 1981-12-3          950                30


现在我们看看这两个table数据存储的位置:


SQL> select dbms_rowid.rowid_block_number(department.rowid) dept_rid,

  2  dbms_rowid.rowid_block_number(employee.rowid) emp_rid,department.deptid

  3  from department,employee

  4  where employee.deptid = department.deptid;


  DEPT_RID    EMP_RID DEPTID

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

      5587       5587     10

      5587       5587     10

      5587       5587     10

      5587       5587     20

      5587       5587     20

      5587       5587     20

      5587       5587     20

      5587       5587     20

      5587       5587     30

      5587       5587     30

      5587       5587     30

      5587       5587     30

      5587       5587     30

      5587       5587     30


可以看到部门ID相同的数据存储在一个block上。我们为什么推荐上述方法对cluster进行最初的装载呢?这样能够保证如果有些cluster key的相关数据超过了size,仍能使大部分数据聚集在一个block上。这只适用于最初的数据装载,在这以后,可以使用事务对cluster中的table insert数据。


    由于cluster中的特殊的数据存储方式,出现了这样一个问题,rowid出现了重复,现在rowid只能在一个table中唯一标识一行数据了(另一种rowid重复出现在transport tablespace的操作后)。

SQL> select rowid from department

  2  intersect

  3  select rowid from employee;


ROWID

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

AAAGWQAADAAABXTAAA

AAAGWQAADAAABXTAAB

AAAGWQAADAAABXTAAC

AAAGWQAADAAABXTAAD


我们还发现,重复的rowid的数量和主表中的记录数一样,即和cluster中的记录数一样。我们把这个blick dump出来,看看是什么原因:


SQL> alter system dump datafile 3 block 5587;


System altered



Dump file D:\database\oracle\admin\ora817\udump\ORA02628.TRC
Wed Dec 31 12:31:10 2003
ORACLE V8.1.7.4.1 - Production vsnsta=0
vsnsql=f vsnxtr=3
Windows 2000 Version 5.0 Service Pack 4, CPU type 586
Oracle8i Enterprise Edition Release 8.1.7.4.1 - Production
With the Partitioning option
JServer Release 8.1.7.4.1 - Production
Windows 2000 Version 5.0 Service Pack 4, CPU type 586
Instance name: ora817

 

Redo thread mounted by this instance: 1

 

Oracle process number: 17

 

Windows thread id: 2628, image: ORACLE.EXE

 


*** SESSION ID:(17.1471) 2003-12-31 12:31:10.159
Start dump data blocks tsn: 2 file#: 3 minblk 4275 maxblk 4275
buffer tsn: 2 rdba: 0x00c010b3 (3/4275)
scn: 0x0000.00142980 seq: 0x01 flg: 0x02 tail: 0x29800601
frmt: 0x02 chkval: 0x0000 type: 0x06=trans data
 
Block header dump:  0x00c010b3
 Object id on Block? Y
 seg/obj: 0x657d  csc: 0x00.14297c  itc: 2  flg: O  typ: 1 - DATA
     fsl: 0  fnx: 0x0 ver: 0x01
 
 Itl           Xid                  Uba         Flag  Lck        Scn/Fsc
0x01   xid:  0x0003.029.000002d2    uba: 0x00800466.006e.35 
C---    0  scn 0x0000.0014297c   ---1.这两个transactionflag lck是什么意思?
0x02   xid:  0x0003.023.000002d2    uba: 0x00800467.006e.37  --U-   18  fsc 0x0000.00142980   
 
data_block_dump
===============
tsiz: 0x1fa0
hsiz: 0x46
pbl: 0x11ab745c
bdba: 0x00c010b3
flag=------K----
ntab=3                           --这是代表5587block上有3table             

                             e_d_cluster,department,employee

nrow=22
frre=-1
fsbo=0x46
fseo=0x1ce3
avsp=0x1c9d
tosp=0x1c9d
0xe:pti[0] nrow=4 offs=0              --这是3table的行数及开始的偏移量
0x12:pti[1] nrow=4 offs=4
0x16:pti[2] nrow=14 offs=8
0x1a:pri[0] offs=0x1f8a              
0x1c:pri[1] offs=0x1eef
0x1e:pri[2] offs=0x1e10
0x20:pri[3] offs=0x1cf9
0x22:pri[4] offs=0x1f72
0x24:pri[5] offs=0x1edb
0x26:pri[6] offs=0x1dfe
0x28:pri[7] offs=0x1ce3
0x2a:pri[8] offs=0x1f4c
0x2c:pri[9] offs=0x1f29
0x2e:pri[10] offs=0x1f05
0x30:pri[11] offs=0x1eb8
0x32:pri[12] offs=0x1e92
0x34:pri[13] offs=0x1e6d
0x36:pri[14] offs=0x1e4a
0x38:pri[15] offs=0x1e26
0x3a:pri[16] offs=0x1dd5
0x3c:pri[17] offs=0x1dac
0x3e:pri[18] offs=0x1d81
0x40:pri[19] offs=0x1d5b
0x42:pri[20] offs=0x1d32
0x44:pri[21] offs=0x1d0f
block_row_dump:
tab 0, row 0, @0x1f8a                                         --从这开始是emp_dept_cluster
tl: 22 fb: K-H-FL-- lb: 0x0 cc: 1
curc: 4 comc: 4 pk: 0x00c010b3.0 nk: 0x00c010b3.0
col  0: [ 2]  c1 0b
tab 0, row 1, @0x1eef
tl: 22 fb: K-H-FL-- lb: 0x0 cc: 1
curc: 6 comc: 6 pk: 0x00c010b3.1 nk: 0x00c010b3.1
col  0: [ 2]  c1 15
tab 0, row 2, @0x1e10
tl: 22 fb: K-H-FL-- lb: 0x0 cc: 1
curc: 7 comc: 7 pk: 0x00c010b3.2 nk: 0x00c010b3.2
col  0: [ 2]  c1 1f
tab 0, row 3, @0x1cf9
tl: 22 fb: K-H-FL-- lb: 0x0 cc: 1
curc: 1 comc: 1 pk: 0x00c010b3.3 nk: 0x00c010b3.3
col  0: [ 2]  c1 29
tab 1, row 0, @0x1f72                                         --从这开始是department
tl: 24 fb: -CH-FL-- lb: 0x2 cc: 2 cki: 0
col  0: [10]  41 43 43 4f 55 4e 54 49 4e 47
col  1: [ 8]  4e 45 57 20 59 4f 52 4b
tab 1, row 1, @0x1edb
tl: 20 fb: -CH-FL-- lb: 0x2 cc: 2 cki: 1
col  0: [ 8]  52 45 53 45 41 52 43 48
col  1: [ 6]  44 41 4c 4c 41 53
tab 1, row 2, @0x1dfe
tl: 18 fb: -CH-FL-- lb: 0x2 cc: 2 cki: 2
col  0: [ 5]  53 41 4c 45 53
col  1: [ 7]  43 48 49 43 41 47 4f
tab 1, row 3, @0x1ce3
tl: 22 fb: -CH-FL-- lb: 0x2 cc: 2 cki: 3
col  0: [10]  4f 50 45 52 41 54 49 4f 4e 53
col  1: [ 6]  42 4f 53 54 4f 4e
tab 2, row 0, @0x1f4c                                  --2从这开始是employee,因为department            

                                          employee都从row 0开始,所以rowid会出现重复

                                       tl: 38 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 0                  --3.这为什么有tran的信息?

 col  0: [ 3]  c2 4e 53
col  1: [ 5]  43 4c 41 52 4b
col  2: [ 7]  4d 41 4e 41 47 45 52
col  3: [ 3]  c2 4f 28
col  4: [ 7]  77 b5 06 09 01 01 01
col  5: [ 3]  c2 19 33
tab 2, row 1, @0x1f29
tl: 35 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 0
col  0: [ 3]  c2 4f 28
col  1: [ 4]  4b 49 4e 47
col  2: [ 9]  50 52 45 53 49 44 45 4e 54
col  3: *NULL*
col  4: [ 7]  77 b5 0b 11 01 01 01
col  5: [ 2]  c2 33
tab 2, row 2, @0x1f05
tl: 36 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 0
col  0: [ 3]  c2 50 23
col  1: [ 6]  4d 49 4c 4c 45 52
col  2: [ 5]  43 4c 45 52 4b
col  3: [ 3]  c2 4e 53
col  4: [ 7]  77 b6 01 17 01 01 01
col  5: [ 2]  c2 0e
tab 2, row 3, @0x1eb8
tl: 35 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 1
col  0: [ 3]  c2 4a 46
col  1: [ 5]  53 4d 49 54 48
col  2: [ 5]  43 4c 45 52 4b
col  3: [ 3]  c2 50 03
col  4: [ 7]  77 b4 0c 11 01 01 01
col  5: [ 2]  c2 09
tab 2, row 4, @0x1e92
tl: 38 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 1
col  0: [ 3]  c2 4c 43
col  1: [ 5]  4a 4f 4e 45 53
col  2: [ 7]  4d 41 4e 41 47 45 52
col  3: [ 3]  c2 4f 28
col  4: [ 7]  77 b5 04 02 01 01 01
col  5: [ 3]  c2 1e 4c
tab 2, row 5, @0x1e6d
tl: 37 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 1
col  0: [ 3]  c2 4e 59
col  1: [ 5]  53 43 4f 54 54
col  2: [ 7]  41 4e 41 4c 59 53 54
col  3: [ 3]  c2 4c 43
col  4: [ 7]  77 b6 0c 09 01 01 01
col  5: [ 2]  c2 1f
tab 2, row 6, @0x1e4a
tl: 35 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 1
col  0: [ 3]  c2 4f 4d
col  1: [ 5]  41 44 41 4d 53
col  2: [ 5]  43 4c 45 52 4b
col  3: [ 3]  c2 4e 59
col  4: [ 7]  77 b7 01 0c 01 01 01
col  5: [ 2]  c2 0c
tab 2, row 7, @0x1e26
tl: 36 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 1
col  0: [ 3]  c2 50 03                                      

col  1: [ 4]  46 4f 52 44
col  2: [ 7]  41 4e 41 4c 59 53 54
col  3: [ 3]  c2 4c 43
col  4: [ 7]  77 b5 0c 03 01 01 01
col  5: [ 2]  c2 1f
tab 2, row 8, @0x1dd5
tl: 41 fb: -CH-FL-- lb: 0x2 cc: 7 cki: 2
col  0: [ 3]  c2 4b 64                                     
col  1: [ 5]  41 4c 4c 45 4e
col  2: [ 8]  53 41 4c 45 53 4d 41 4e
col  3: [ 3]  c2 4d 63
col  4: [ 7]  77 b5 02 14 01 01 01
col  5: [ 2]  c2 11
col  6: [ 2]  c2 04
tab 2, row 9, @0x1dac
tl: 41 fb: -CH-FL-- lb: 0x2 cc: 7 cki: 2
col  0: [ 3]  c2 4c 16
col  1: [ 4]  57 41 52 44
col  2: [ 8]  53 41 4c 45 53 4d 41 4e
col  3: [ 3]  c2 4d 63
col  4: [ 7]  77 b5 02 16 01 01 01
col  5: [ 3]  c2 0d 33
col  6: [ 2]  c2 06
tab 2, row 10, @0x1d81
tl: 43 fb: -CH-FL-- lb: 0x2 cc: 7 cki: 2
col  0: [ 3]  c2 4d 37
col  1: [ 6]  4d 41 52 54 49 4e
col  2: [ 8]  53 41 4c 45 53 4d 41 4e
col  3: [ 3]  c2 4d 63
col  4: [ 7]  77 b5 09 1c 01 01 01
col  5: [ 3]  c2 0d 33
col  6: [ 2]  c2 0f
tab 2, row 11, @0x1d5b
tl: 38 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 2
col  0: [ 3]  c2 4d 63
col  1: [ 5]  42 4c 41 4b 45
col  2: [ 7]  4d 41 4e 41 47 45 52
col  3: [ 3]  c2 4f 28
col  4: [ 7]  77 b5 05 01 01 01 01
col  5: [ 3]  c2 1d 33
tab 2, row 12, @0x1d32
tl: 41 fb: -CH-FL-- lb: 0x2 cc: 7 cki: 2
col  0: [ 3]  c2 4f 2d
col  1: [ 6]  54 55 52 4e 45 52
col  2: [ 8]  53 41 4c 45 53 4d 41 4e
col  3: [ 3]  c2 4d 63
col  4: [ 7]  77 b5 09 08 01 01 01
col  5: [ 2]  c2 10
col  6: [ 1]  80
tab 2, row 13, @0x1d0f
tl: 35 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 2
col  0: [ 2]  c2 50
col  1: [ 5]  4a 41 4d 45 53
col  2: [ 5]  43 4c 45 52 4b
col  3: [ 3]  c2 4d 63
col  4: [ 7]  77 b5 0c 03 01 01 01
col  5: [ 3]  c2 0a 33
end_of_block_dump
End dump data blocks tsn: 2 file#: 3 minblk 5587 maxblk 5587


现在我们来说明这个block中的几个问题:


1.这两个transactionflag lck是什么意思?

这里是blockITL的信息,这里初始有两个事务,事务槽在block有空间时是可以扩展的。

falg  ------ 依次4个位置是   CBUT
 C 
表示该 ITL 事务已经提交

B: 当读到这个ITL 的时候该block需要 rollback (一致读)导致ITL 来自 rollback segment,也就是说该ITL事务被提交过多次了需要追述到最早的部分
U   表示该ITL 事务已经提交,但是提交的SCNoracle 在数据有效性的基础上出来的,可能并不是真实的 commit  scn ,这个SCN就是 scn/fsc  . 
T : 
事务提交后ITL涉及到的数据行上的 lock信息也就是 lb ,在下次dml操作到该块数据的时候,lb 会清零,表示清除lock(当然即使没有清除该ITL若已经提交也认为锁不存在),在这个清理的过程中相应flag位置会设置为 T

lck  表示该 itl 在该block上影响到的行数

2.为什么rowid会出现重复?

   我们想知道rowid为什么会重复,首先要明白rowid的含义。ROWID 在磁盘上需要10 个字节的存储空间并使用18 个字符来显示它包含下列组件

  • 数据对象编号每个数据对象如表或索引在创建时都分配有此编号,并且此编号在数据库中是唯一的;
  • 相关文件编号此编号对于一个表空间中的每个文件是唯一的;
  • 块编号表示包含此行的块在文件中的位置;
  • 行编号标识块头中行目录位置的位置;

在内部数据对象编号需要32 位,相关文件编号需要10 位,块编号需要22,位行编号需要16 位,加起来总共是80 位或10 个字节,ROWID 使用以64 为基数的编码方案来显示该方案将六个位置用于数据对象,编号三个位置用于相关文件编号六个位置用于块编号三个位置用于行编号以64 为基数的编码方案使用字符A-Z a-z 0-9 + 和/共64 个字符如下例所示

AAAGWQ AAD AAABXT AAA

在本例中

AAAGWQ 是数据对象编号

AAD    是相关文件编号

AAABXT 是块编号

AAA    是行编号

在block 5587中delparment和employee中行编号都是从0开始,文件编号和块编号相同,我们再来看一下数据对象编号:

SQL> select OBJECT_NAME,OBJECT_ID ,DATA_OBJECT_ID from user_objects

  2  where OBJECT_NAME in ('DEPARTMENT','EMPLOYEE','E_D_CLUSTER');


OBJECT_NAME               OBJECT_ID        DATA_OBJECT_ID

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

E_D_CLUSTER               26000             26000

EMPLOYEE                  26004             26000

DEPARTMENT                26002             26000

从这里能看出对象编号虽然不一样,但在一个cluster中数据对象编号是一样的。既然构成rowid的组件都是一样的,那么rowid自然是相同的。


3.这为什么有tran的信息?

tl: 38 fb: -CH-FL-- lb: 0x2 cc: 6 cki: 0

lb: 0x2 表示改行上有事务0x02,这就是oracle的行锁,如果该行上没有事务, lb: 0x0,即没有锁。但这行数据是否有锁还倚赖于该事务是不是活动的,这里虽然lb: 0x2 ,但我们在ITL中可以看到事务0x02状态为--U-,表示事务已经提交,即非活动事务,所以这行数据上并没有锁。

你可能感兴趣的:(数据库-Oracle中聚簇表的使用)