索引组织表简单介绍

来源:互联网 发布:怎样开个淘宝店铺 编辑:程序博客网 时间:2024/06/06 07:30

索引组织表(Index Organizied Table

索引组织表(index organized table, IOT)就是存储在一个索引结构中的表。存储在堆中的表是无组织的(也就是说,只要有可用的空间,数据可以放在任何地方)IOT中的数据则按主键存储和排序。对你的应用来说,IOT表和一个常规表并无二致。

索引组织表的数据按主键排序手段被存储在B-树索引中,除了存储主键列值外还存储非键列的值。普通索引只存储索引列,而索引组织表则存储表的所有列的值。

索引组织表一般适应于静态表,且查询多以主键列。当表的大部分列当作主键列时,且表相对静态,比较适合创建索引组织表!(8i以上)

既然它属于表,那么它当然也有建立索引的需求。由于它的索引的结构,比如说由于索引叶节点的分裂,行所在块可能会发生改变,因而建立在IOT上的索引和一般的索引的最大区别是它存的IOT的行的逻辑地址,也就是UROWIDoracle用这个逻辑rowid来猜这个行所在的块,如果猜到了,那么这个urowid是正确的,否则它从这个地址向下遍历来找这条记录。

IOT表的rowid是逻辑上的,因为IOT表中的行的位置是在不断变化的(例如插入新的行,有可能带来其它行的位置移动)

    IOT有什么意义呢?使用堆组织表时,我们必须为表和表主键上的索引分别留出空间。而IOT不存在主键的空间开销,因为索引就是数据,数据就是索引,二者已经合二为一。但是,IOT带来的好处并不止于节约了磁盘空间的占用,更重要的是大幅度降低了I/O,减少了访问缓冲区缓存(尽管从缓冲区缓存获取数据比从硬盘读要快得多,但缓冲区缓存并不免费,而且也绝对不是廉价的。每个缓冲区缓存获取都需要缓冲区缓存的多个闩,而闩是串行化设备,会限制应用的扩展能力)

     IOT
适用的场合有:

  1
、完全由主键组成的表。这样的表如果采用堆组织表,则表本身完全是多余的开销,因为所有的数据全部同样也保存在索引里,此时,堆表是没用的。
  2
、代码查找表。如果你只会通过一个主键来访问一个表,这个表就非常适合实现为IOT.
  3
、如果你想保证数据存储在某个位置上,或者希望数据以某种特定的顺序物理存储,IOT就是一种合适的结构。

    IOT提供如下的好处:
  ·
提高缓冲区缓存效率,因为给定查询在缓存中需要的块更少。
  ·
减少缓冲区缓存访问,这会改善可扩缩性。
  ·
获取数据的工作总量更少,因为获取数据更快。
  ·
每个查询完成的物理I/O更少,因为对于任何给定的查询,需要的块更少,而且对地址记录的一个物理 I/O 很可能可以获取所有地址(而不只是其中一个地址,但堆表实现就只是获取一个地址)

    如果经常在一个主键或惟一键上使用BETWEEN 查询也是如此,因为相近的记录存在一起,查询时引入的逻辑IO和物理IO都会更少。

NOCOMPRESS 选项

 

这个选项对索引一般都可用。它告诉 Oracle 把每个值分别存储在各个索引条目中(也就是不压缩)。如果对象的主键在 A和 列上, A和 的每一次出现都会物理地存储。 NOCOMPRESS 反过来就是 COMPRESS ,在此 是一个整数,表示要压缩的列数。这样可以避免重复值,并在块级提取 “公因子”( factor out )。这样在 的值(以及 的值)重复出现时,将不再物理地存储它们。

OVERFLOW&PCTTHRESHOLD&INCLUDING选项

OVERFLOW 子句允许你建立另一个段(这就使得 IOT 成为一个多段对象,就像有一个 CLOB 列一样),如果 IOT 的行数据变得太大,就可以溢出到这个段中。

注意:构成主键的列不能溢出,它们必须直接放在叶子块上。

 

PCTTHRESHOLD :行中的数据量超过块的这个百分比时,行中余下的列将存储在溢出段中。所以,如果 PCTTHRESHOLD 是 10% ,而块大小是 8KB ,长度大于 800 字节的行就会把其中一部分存储在别处,而不能在索引块上存储。

INCLUDING :行中从第一列直到 INCLUDING 子句所指定列(也包括这一列)的所有列都存储在索引块上,余下的列存储在溢出段中。

 

对于 IOT 最后要考虑的是建立索引。 IOT 本身可以有一个索引,就像在索引之上再加索引,这称为二次索引( secondary index )。 正常情况下,索引包含了所指向的行的物理地址,即 rowid 。而 IOT 二次索引无法做到这一点;它必须使用另外某种方法来指示行的地址。这是因为 IOT 中 的行可以大量移动, 而且它不像堆组织表中的行那样 “ 迁移 ” 。 IOT 中的行肯定在索引结构中的每个位置上,这取决于它的主键值;只有当索引本身的大小和形状 发生改变时行才会移动(下一章将更详细地讨论索引结构如何维护)。

    为了适应这种情况, racle 引入了一个逻辑 rowid ( logical rowid )。 这些逻辑 rowid 根据 IOT 主键建立。对于行的当前位置还可以包含一个 “ 猜测 ” ,不过这个猜测几乎是错的,因为稍过一段时间后, IOT中的数据可能就会 移动。这个猜测是行第一次置于二次索引结构中时在 IOT 中的物理地址。如果 IOT 中 的行必须移动到另外一个块上,二次索引中的猜测就会变得 “ 过时 ” 。因 此,与常规表相比, IOT 上的索 引效率稍低。在一个常规表上,索引访问通常需要完成一个 I/O 来扫描索引结构,然后需要一个读来读取表数据。对于 IOT , 通常要 完成两个扫描;一次扫描二次结构,另一次扫描 IOT 本身。除此之外, IOT 上的索引可以使用非主键列提供 IOT 数据的快速、高效访问。

索引组织表小结

     建立 IOT 时,最关键的是适当地分配数据,即哪些数据存储在索引块上,哪些数据存储在溢出段上。对溢出条件不同的各种场景进行基准测试,查看对 INSERT 、 UPDATE 、 DELETE 和 SELECT 分别有怎样的影响。如果结构只建立一次,而且要频繁读取,就应该尽可能地把数据放在索引块上(最合适获取),要么频繁地组织索引中的数据(不适于修改)。堆表的 freelist 相关考虑对 IOT 也同样适用。 PCTFREE PCTUSED 在 IOT 中 是两个重要的角色。不过, PCTFREE 对于 IOT 不像对于堆表那么重要,另外 PC TUSED 一般不起作用。不过,考虑 OVERFLOW 段时, PCTFREE 和 PCTUSED 对于 IOT 的意义将与对于堆表一样重大;要采用与堆表相同的逻辑为溢出段设置这两个参数。

 

--堆表--
DROP TABLE ONE_MONTH_BOSS_UPLOAD_TIMES
/
CREATE TABLE ONE_MONTH_BOSS_UPLOAD_TIMES(
CHILDECID VARCHAR2(11),
UP_TIMES  NUMBER(3)
)
/
CREATE INDEX ONE_MONTH_BOSS_UPLOAD_TIMES_M
           ON ONE_MONTH_BOSS_UPLOAD_TIMES(CHILDECID)
           TABLESPACE INDX
           /
INSERT INTO ONE_MONTH_BOSS_UPLOAD_TIMES
           SELECT A.CHILDECID,COUNT(*) 
           FROM
             
           SELECT B.CHILDECID
           FROM BOSS_UPLOAD B
           WHERE B.PRODID=1
           AND B.OPRCODE='03'
           AND B.CREATEDATE>=TRUNC(SYSDATE)-30                
           UNION ALL
           SELECT B2.CHILDECID
           FROM BOSS_UPLOAD_HIS B2
           WHERE B2.PRODID=1
           AND B2.OPRCODE='03'
           AND B2.CREATEDATE>=TRUNC(SYSDATE)-30
           ) A
           GROUP BY A.CHILDECID
           /
           SELECT SUM(BYTES)/1024/1024
FROM DBA_SEGMENTS DS
WHERE OWNER='EDU'
AND DS.SEGMENT_NAME='ONE_MONTH_BOSS_UPLOAD_TIMES'
/96
SELECT SUM(BYTES)/1024/1024
FROM DBA_SEGMENTS DS
WHERE OWNER='EDU'
AND DS.SEGMENT_NAME='ONE_MONTH_BOSS_UPLOAD_TIMES_M'
106

--索引组织表--     
DROP TABLE ONE_MONTH_BOSS_UPLOAD_TIMES2
/
CREATE TABLE ONE_MONTH_BOSS_UPLOAD_TIMES2(
CHILDECID VARCHAR2(11)   CONSTRAINT PK_UPLOAD_TIMES PRIMARY KEY,
UP_TIMES  NUMBER(3)
)
ORGANIZATION INDEX
 TABLESPACE INDX;
/
INSERT INTO ONE_MONTH_BOSS_UPLOAD_TIMES2
           SELECT A.CHILDECID,COUNT(*) 
           FROM
             
           SELECT B.CHILDECID
           FROM BOSS_UPLOAD B
           WHERE B.PRODID=1
           AND B.OPRCODE='03'
           AND B.CREATEDATE>=TRUNC(SYSDATE)-30                
           UNION ALL
           SELECT B2.CHILDECID
           FROM BOSS_UPLOAD_HIS B2
           WHERE B2.PRODID=1
           AND B2.OPRCODE='03'
           AND B2.CREATEDATE>=TRUNC(SYSDATE)-30
           ) A
           GROUP BY A.CHILDECID
/
SELECT SUM(BYTES)/1024/1024
FROM DBA_SEGMENTS DS
WHERE OWNER='EDU'
AND DS.SEGMENT_NAME='ONE_MONTH_BOSS_UPLOAD_TIMES2'
/0
SELECT SUM(BYTES)/1024/1024
FROM DBA_SEGMENTS DS
WHERE OWNER='EDU'
AND DS.SEGMENT_NAME='PK_UPLOAD_TIMES'
176
          
           SELECT COUNT(*)
           FROM INVALID_STU_TJ
          
           ALTER TABLE INVALID_STU_TJ ADD UP_TIMES NUMBER(3)
           /
          
UPDATE INVALID_STU_TJ TJ
SET UP_TIMES=
(
SELECT T.UP_TIMES
FROM ONE_MONTH_BOSS_UPLOAD_TIMES T
WHERE T.CHILDECID=TJ.CON1_MOBILE
)
/
BEGIN
DBMS_STATS.GATHER_TABLE_STATS('EDU','ONE_MONTH_BOSS_UPLOAD_TIMES2', CASCADE => TRUE);
END;
/
UPDATE INVALID_STU_TJ TJ
SET UP_TIMES=
(
SELECT T.UP_TIMES
FROM ONE_MONTH_BOSS_UPLOAD_TIMES2 T
WHERE T.CHILDECID=TJ.CON1_MOBILE
)
/

           --堆表,有索引,写入数据4045764,时间:2MIN51SEC
           --占用空间:表96M,索引106M
           --更新1010261万数据,扫描方式为NEST LOOP,驱动表数据量1010261万,该表作为内部表,走索引,更新时间:3MIN48SEC
          
           --索引组织表,写入数据4045764,时间:3MIN30SEC
           --占用空间:表0,索引176M
            --更新1010261万数据,扫描方式为NEST LOOP,驱动表数据量1010261万,该表作为内部表,走索引,更新时间:2MIN47SEC