转:http://blog.itpub.net/17203031/viewspace-744477

对关系型数据库产品(RDBMS)而言,一个重要特性就是:数据信息都被组织为二维数据表,信息的表达可以通过一系列的关联(Join)来完成。具体数据库产品在实现这个标准的时候,又有千差万别的特点。就是一个特定的数据库RDBMS产品,往往也提供不同的实现方法。

1、从堆表(Heap Table)到索引组织表(Index Organization Table)

Oracle作为一款成熟的数据库软件产品,就提供了多种数据表存储结构。我们最常见的就是三种,分别为堆表(Heap Table)、索引组织表(Index Organization Table,简称为IOT)和聚簇表(Cluster Table)。

Heap Table是我们在Oracle中最常使用的数据表,也是Oracle的默认数据表存储结构。在Heap Table中,数据行是按照“随机存取”的方式进行管理。从段头块之后,一直到高水位线一下的空间,Oracle都是按照随机的方式进行“粗放式”管理。当一条数据需要插入到数据表中时,默认情况下,Oracle会在高水位线以下寻找有没有空闲的地方,能够容纳这个新数据行。如果可以找到这样的地方,Oracle就将这行数据放在空位上。注意,这个空位选择完全依“能放下”的原则,这个空位可能是被删除数据行的覆盖位。

如果Heap Table段的HWM下没有找到合适的位置,Oracle堆表才去向上推高水位线。在数据行存储上,Heap Table的数据行是完全没有次序之分的。我们称之为“随机存取”特征。

对Heap Table,索引独立段的添加一般可以有效的缓解由于随机存取带来的检索压力。Index叶子节点上记录的数据行键值和Rowid取值,可以让Server Process直接定位到数据行的块位置。

聚簇(Cluster Table)是一种合并段存储的情况。Oracle认为,如果一些数据表更新频率不高,但是经常和另外一个数据表进行连接查询(Join)显示,就可以将其组织在一个存储结构中,这样可以最大限度的提升性能效率。对聚簇表而言,多个数据表按照连接键的顺序保存在一起。

通常系统环境下,我们使用Cluster Table的情况不太多。Oracle中的数据字典大量的使用聚簇。相比是各种关联的基表之间固定连接检索的场景较多,从而确定的方案。

最后就是本系列的IOT(Index Organization Table)。同Cluster Table一样,IOT是在Oracle数据表策略的一种“非主流”,应用的场景比较窄。但是一些情况下使用它,往往可以起到非常好的效果。

简单的说,IOT区别于堆表的最大特点,就在于数据行的组织并不是随机的,而是依据数据表主键,按照索引树进行保存。从段segment结构上看,IOT索引段就包括了所有数据行列,不存在单独的数据表段。

IOT在保存结构上有一些特殊之处,应用在一些特殊的场景之下。本系列将逐个分析IOT的一些特征,最后讨论我们究竟在什么样的场景下,可以选择IOT作为数据表方案。

2、IOT基础

在创建使用IOT上,我们要强调Primary Key的作用。对一般的堆表而言,Primary Key是可有可无的。一种说法是:当一个堆表没有设置主键的时候,rowid伪列就是对应的主键值。而且,Primary Key可以在数据表创建之后进行追加设置。

但是,IOT对于主键的设置格外严格,要求创建表的时候就必须指定明确的主键列。下面我们通过一系列的实验来证明,实验环境为Oracle 11g。

SQL> select * from v$version;

BANNER

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

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

PL/SQL Release 11.2.0.1.0 - Production

CORE    11.2.0.1.0  Production

我们使用相同的结构,来创建出IOT和Heap Table对照。

--不指定主键,是无法创建IOT;

SQL> create table m (id number) organization index;

create table m (id number) organization index

ORA-25175: 未找到任何 PRIMARY KEY 约束条件

在create table语句后面使用organization index,就指定数据表创建结构是IOT。但是在不指定主键Primary Key的情况下,是不允许建表的。

SQL> create table t_iot (object_id number(10) primary key, object_name varchar2(100)) organization index;

Table created

SQL> create table t_heap (object_id number(10) primary key, object_name varchar2(100));

Table created

(插入相同数据来源行……)

SQL> exec dbms_stats.gather_table_stats(user,'T_IOT',cascade => true);

PL/SQL procedure successfully completed

SQL> exec dbms_stats.gather_table_stats(user,'T_HEAP',cascade => true);

PL/SQL procedure successfully completed

从数据字典的层面上,我们分析一下两个数据表的差异,一窥IOT的特点。

SQL> select table_name, tablespace_name, blocks, num_rows from user_tables where table_name in ('T_IOT','T_HEAP');

TABLE_NAME          TABLESPACE_NAME                    BLOCKS   NUM_ROWS

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

T_HEAP                SYSTEM                                157      72638

T_IOT                                                                72638

SQL> select segment_name, blocks, extents from user_segments where segment_name in ('T_IOT','T_HEAP');

SEGMENT_NAME             BLOCKS    EXTENTS

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

T_HEAP                      256         17

上面两句SQL揭示了几个问题。首先,Oracle承认IOT是一个数据表,并且统计了数据行数。但是对数据表的存储表空间和大小没有明确的说明,user_tables视图中这部分的内容为空。

其次,从段结构来看,Oracle明确不承认存在T_IOT段。因为如果有段segment对象,就意味有空间分配。但是数据表有数据,是存放在哪里呢?

我们知道,给数据表添加索引的时候,Oracle会自动的添加一个唯一索引。那么我们去检查一下这部分的结构情况。

SQL> select index_name, index_type, table_name, PCT_THRESHOLD, CLUSTERING_FACTOR from user_indexes where table_name in ('T_IOT','T_HEAP');

INDEX_NAME    INDEX_TYPE           TABLE_NAME PCT_THRESHOLD CLUSTERING_FACTOR

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

SYS_C0012408    NORMAL               T_HEAP                                 256

SYS_IOT_TOP_75124 IOT - TOP      T_IOT                 50                 0

SQL> select segment_name, blocks, extents from user_segments where segment_name in ('SYS_C0012408','SYS_IOT_TOP_75124');

SEGMENT_NAME             BLOCKS    EXTENTS

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

SYS_C0012408                256         17

SYS_IOT_TOP_75124           256         17

索引段是存在的,而且明确标注索引类型为IOT索引。这说明几个问题:

首先,对于IOT而言,只有索引段,没有数据段。一般的索引而言,叶子节点上只有索引列的取值和rowid。而对于IOT而言,主键索引上对应就是数据行和索引列取值。

其次,IOT的溢出段阈值(PCT_THRESHOLD)。这是Oracle IOT的特殊策略。简单的说,当我们把全部数据行保存在叶子节点上,一旦发生主键值的变化、新值插入、删除等动作,索引叶子块的分裂动作是频繁的。数据行保存在叶子节点上只会让这样的分裂动作更加频繁和后果严重。Oracle提出将一部分的非主键列单独存储,这个参数就是比例值。

最后,我们探讨一下IOT索引的Clustering Factor。Clustering Factor是反映索引叶子节点顺序和数据保存行直接离散程度的综合性指标。一般来说,堆表的Clustering Factor是随着DML操作不断退化的过程。Clustering Factor是影响到Oracle索引路径成本的一个重要参数(http://space.itpub.net/17203031/viewspace-680936),会影响到CBO的成本决策。 IOT的索引这部分的值永远为0,因为索引的顺序就是数据行的顺序,两者存储顺序相同,绝对一致。

3、IOT与执行计划

在IOT数据表下,我们通常的执行计划会如何呢?普通Heap Table和IOT在这部分的差异很大。

通常而言,Heap Table的索引路径伴随着两次段结构的读取——索引段和数据段。先读取索引段段头,经历根节点、分支节点、叶子节点,最后获取到结果集合rowid列表。之后进行回表操作,使用rowid依次查询数据表的行。

但是IOT表可以不同。索引和数据保留在一起,理论上拿到了叶子节点,也就是拿到了数据行。IOT是不存在回表操作的,所以相对heap table来说,回表部分成本是节省的。

下面我们通过执行计划,来看IOT的特征。

SQL> explain plan for select * from t_iot where object_id=1000;

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

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

Plan hash value: 2277898128

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

| Id  | Operation         | Name             | Rows  | Bytes | Cost (%CPU)| Tim

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

|   0 | SELECT STATEMENT  |                 |     1 |    11 |     1   (0)| 00:

|*  1 |  INDEX UNIQUE SCAN| SYS_IOT_TOP_75124 |    1 |    11 |     1   (0)| 00:

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

Predicate Information (identified by operation id):

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

1 - access("OBJECT_ID"=1000)

13 rows selected

SQL> explain plan for select * from t_iot;

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

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

Plan hash value: 4201110863

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

| Id  | Operation            | Name              | Rows  | Bytes | Cost (%CPU)|

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

|   0 | SELECT STATEMENT     |                   | 72638 |   780K|    47   (0)|

|   1 |  INDEX FAST FULL SCAN| SYS_IOT_TOP_75124 | 72638 |   780K|    47   (0)|

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

8 rows selected

对于IOT,我们要保证访问的数据表的方式是主键路径为主。在上面的两个执行计划中,我们按照主键进行检索,路径为Index Unique Scan。全表扫描为Index Fast Full Scan。两者都没有明显的回表动作。

试想,如果数据表较小,Index Full Scan也是IOT表常常出现的执行路径。

对一般的Heap Table,执行路径如何呢?

SQL> explain plan for select * from t_heap where object_id=1000;

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

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

Plan hash value: 1833345710

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

| Id  | Operation                | Name         | Rows  | Bytes | Cost (%CPU)

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

|   0 | SELECT STATEMENT           |              |     1 |    11 |     2   (0)

|   1 |  TABLE ACCESS BY INDEX ROWID| T_HEAP      |     1 |    11 |     2   (0)

|*  2 |   INDEX UNIQUE SCAN        | SYS_C0012408 |     1 |       |     1   (0)

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

Predicate Information (identified by operation id):

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

2 - access("OBJECT_ID"=1000)

14 rows selected

SQL> explain plan for select * from t_heap;

Explained

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

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

Plan hash value: 1253663840

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

| Id  | Operation         | Name   | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT  |        | 72638 |   780K|    42   (0)| 00:00:01 |

|   1 |  TABLE ACCESS FULL| T_HEAP | 72638 |   780K|    42   (0)| 00:00:01 |

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

8 rows selected

普通堆表都不能避免出现回表动作。

最后,我们要声明一下回表动作的成本影响。IOT和Heap Table一个很大的执行计划差异,就是回表。但是从成本上计算,CBO并不是因为回表动作才确定执行计划,而是Clustering Factor的影响。

对堆表而言,Clustering Factor都是一个很大的问题,无论是CBO的成本公式上,还是不断Degrade的前景。IOT一个突出优势就是直接消灭了Clustering Factor的成本因素。

但是这也就带来一个问题,一个数据表只能按照主键的顺序进行组织,辅助索引(Secondary Index)的问题是很多版本Oracle和IOT使用者争议的话题。Secondary Index问题我们在后面会继续讨论到。

下篇中我们会继续讨论有关IOT维护等其他内容。

简述Oracle IOT(Index Organized Table)的更多相关文章

  1. IOT(Index Organized Table)

    我们知道一般的表都以堆(heap)的形式来组织的,这是无序的组织方式.Oracle还提供了一种有序的表,它就是索引组织表,简称IOT表.IOT表上必须要有主键,而IOT表本身不对应segment,表里 ...

  2. Oracle composite index column ordering

    Question:  I have a SQL with multiple columns in my where clause.  I know that Oracle can only choos ...

  3. Oracle临时表(Temporary Table)

    GLOBAL TEMPORARY代表全局临时表临时表的元数据存储在数据字典里面 只当第一条DML命令发生的时候才为这张表的段分配空间 临时表数据的可见范围应该是会话级别或是事务级别的 会话或者事务级别 ...

  4. Oracle基础 10 表 table

    --查看表的结构 desc ygb; select * from user_tab_columnswhere table_name='YGB'; --新建表ygb create table ygb(  ...

  5. Oracle alter index rebuild 与 ORA-08104 说明

    在ITPUB 论坛上看到的一个帖子,很不错.根据论坛的帖子重做整理了一下. 原文链接如下: alter index rebuild online引发的血案 http://www.itpub.net/t ...

  6. oracle alter index rebuild offline与online

    oracle index build online与offline测试环境为oracle 11.2.0.4 --sql test SQL> conn test/test )); begin .. ...

  7. Oracle临时表GLOBAL TEMPORARY TABLE

    临时表:像普通表一样,有结构,但是对数据的管理上不一样,临时表存储事务或会话的中间结果集,临时表中保存的数据只对当前 会话可见,所有会话都看不到其他会话的数据,即使其他会话提交了,也看不到.临时表不存 ...

  8. Oracle 11G INDEX FULL SCAN 和 INDEX FAST FULL SCAN 对比分析

    SQL> drop table test; 表已删除. SQL> create table test as select * from dba_objects where 1!=1; 表已 ...

  9. ORACLE中index的rebuild(转)

    Oracle里大量删除记录后,表和索引里占用的数据块空间并没有释放. table move可以释放已删除记录表占用的数据块空间,整理碎片.如果将表格用move方式整理碎片后,索引将失效,这时需要将索引 ...

随机推荐

  1. oslo_db使用

    oslo_db是openstak中封装数据库访问sqlachmy的模块,网上搜索的资源并不多,除了openstack官方文档,在实际使用中的例子凤毛麟角. 有感于资源太少,在学习heat源码的过程中, ...

  2. javaMail实现收发邮件(四)

    JavaMail API中也提供了一些专门的类来对邮件的接收进行相关的操作,在介绍这些类之前,我们先来了解下邮件接收API的体系结构,JavaMai API中定义了一个java.mail.Store类 ...

  3. webpack工程搭建

    >>建立nodejs工程 新建文件夹,npm init 生成package.json >>安装webpack 和 webpack-dev-server npm install ...

  4. select简单循环嵌套

    访问学生的物理最高成绩,并且打印出来,单个要打印出所有的信息 在添加几个 and 就可以啦. select  student.gender,student.sname from student whe ...

  5. Java中的属性和方法

    题目 实体类 测试类

  6. Kubernetes helm配置国内镜像源

    1.删除默认的源 helm repo remove stable 2.增加新的国内镜像源 helm repo add stable https://burdenbear.github.io/kube- ...

  7. python、java读数据

    python从txt文档中读数据有个特别神奇的函数 可以把txt文档中的数据直接读取成python数组 java用Scanner类读数据比较方便

  8. import os, glob, fnmatch--Python os/glob/fnmatch主要函数总结

    auther: Lart date: 2019-01-17 update: 2019-01-18 09:55:36 --- import os, glob, fnmatch 针对某些操作, 官方推荐这 ...

  9. Learning to Promote Saliency Detectors

    Learning to Promote Saliency Detectors 原本放在了思否上, 但是公式支持不好, csdn广告太多, 在博客园/掘金上发一下 https://github.com/ ...

  10. python 12 模块与包

    一.不知道什么原则 python文件下面只写方法,所有的可直接执行的代码,都放在条件下 原因.该文件有可能被其他文件调用 二.eval() 将字符串转为相应的数据格式可以想象json转为map 三.模 ...