索引組織表的概述:
在Oracle數據庫中,有一類表被稱之爲索引組織表,即IOT(Index-Organized Table)。顧名思義,所謂的索引組織表, 表面上看是一種表,實質上其數據是以索引的形式來存放的,也就是說IOT表不會佔用表段,其佔用的是索引段。
相比較傳統的堆表( heap-organized table,即常見的普通表)而言,IOT表的數據是以已經按主鍵字段排好序後存放在B-tree索引中的,而堆表的數據則是無序的存放在表段的數據塊中。此外,在IOT的索引葉子節點塊中,既存放主鍵字段數據,也存放非主鍵字段的值。
二: 索引組織表(IOT Index-Organized Table)特徵:
通過IOT表的主鍵字段來訪問數據可以快速完成,因爲IOT表的數據全部存放在B-Tree索引上,只需定位到索引上的數據即可,而無需再去像訪問heap表那樣進一步通過索引去定位表段上的數據;
對IOT表執行DML操作,只會影響到B-Tree索引; 通過IOT表的主鍵字段快速範圍訪問數據很快,因爲記錄已經是事先按主鍵排好序的;
IOT表可以有效的降低存儲開銷,因爲主鍵字段的數據只是存放在B-Tree索引上, 並沒有像heap那樣,主鍵字段數據既存放在表段上,也存放在索引上;
IOT表除了像Heap表那樣可以支持約束、觸發器、LOB字段、對象字段、分區、並行操作、在線重定義、複製操作等,還支持主鍵字段壓縮、提供溢出存儲段(Overflow storage area )、二次索引(Secondary indexes, including bitmap indexes)
三 :創建索引組織表(IOT Index-Organized Table)
創建IOT表時,必須包含下列2個從句:
1 ORGANIZATION INDEX,用來標識該表是IOT表;
2 在建表的同時要指定主鍵約束,可以是單字段主鍵,也可以是複合主鍵約束。
創建IOT表時,也可以同時指定下列3個從句:
1 OVERFLOW從句,用於標識非主鍵字段存放在獨立的溢出存儲段數據區;
2 PCTTHRESHOLD value,如果指定了溢出存儲段的話,該值用於限定可以存放在索引數據塊中的最大數據的百分比,
即如果IOT表中的行記錄超過該值的話,剩餘的字段就存放在溢出存儲段數據區。也就是說,
IOT表中的一條記錄有可能被拆分成兩部分:頭數據區(Head Piece)和尾數據區(Tail Piece)。將主鍵字段和不超過PCTTHRESHOLD限定的其它非主鍵字段存放在頭數據區,
而將其它的非主鍵字段存放在尾數據區。因此,此時的IOT表的索引記錄存放的數據就成了主鍵字段+滿足PCTTHRESHOLD限定的其它非主鍵字段+指針,
指針指向剩餘非主鍵字段存放的地址;PCTTHRESHOLD的取值範圍是1-50,默認值是50;
3 INCLUDING從句,用於顯示聲明哪些非主鍵字段可以和主鍵字段一起存放在索引數據塊中。這樣,剩下的非主鍵字段就會存放到獨立的溢出存儲段數據區
創建一個索引表:
SQL> create table iot_table(owner,object_type,object_name,object_id,
2 constraint iot_pk primary key(object_id,object_type,object_name)
3 )
4 organization index
5 tablespace test
6 pctthreshold 50
7 overflow tablespace users
8 as
9 select distinct owner,object_type,object_name,object_id from all_objects;
查看錶的信息,可以看到該表不屬於任何表空間,因爲沒有佔用數據段
SQL> select table_name,tablespace_name,iot_name,iot_type from user_tables where table_name='IOT_TABLE';
TABLE_NAME TABLESPACE_NAME IOT_NAME
------------------------------------------------------------ ------------------------------------------------------------ ------------------------------------------------------------
IOT_TYPE
------------------------
IOT_TABLE
IOT
能看到我們的表名和IOT_TYPE 類型,其他信息均沒有
然後查看我們的索引信息
SQL> select index_name,index_type,table_name,tablespace_name,table_type,pct_threshold from user_indexes where table_name='IOT_TABLE';
INDEX_NAME INDEX_TYPE TABLE_NAME
------------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------------
TABLESPACE_NAME TABLE_TYPE PCT_THRESHOLD
------------------------------------------------------------ ---------------------- -------------
IOT_PK IOT - TOP IOT_TABLE
TEST
通過這個語句,就可以看到我們佔用的表空間是test,index_name是IOT_PK
這個就是我們文章開頭說的,IOT表的數據存儲在索引塊中,包括主鍵列和非主鍵列
四:索引組織表適用的場景
先通過對比普通的堆表(heap)和IOT表的列子,來引用IOT表適用的場景
創建一張表,當做母表
SQL> create table emp as select object_id e_id,
2 object_name ename,
3 created hiredate,
4 owner job
5 from all_objects;
SQL> select count(*) from emp;
COUNT(*)
----------
65581
增加一個主鍵:SQL> alter table emp add constraint emp_pk primary key(e_id);
對錶進行分析,使用dbms的包,獲取的結果要比使用analyze分析的要準確
SQL> exec dbms_stats.gather_table_stats(user,'EMP',cascade=>true);
PL/SQL procedure successfully completed.
Cascade 是分析表的鍵和列信息
基於emp,創建一張heap的子表和一張iot的子表
創建一個普通表
create table heap_table (
2 empno references emp(e_id) on delete cascade,
3 add_type varchar2(15),
4 street varchar2(15),
5 city varchar2(10),
6 state varchar2(5),
7 zip number,
8 primary key (empno,add_type)
9 );
SQL> create table iot_address (
2 empno references emp(e_id) on delete cascade,
3 add_type varchar2(15),
4 street varchar2(15),
5 city varchar2(10),
6 state varchar2(5),
7 zip number,
8 primary key (empno,add_type)
9 )
10 organization index;
往兩個表中插入數據,數據都是雜亂的,主要是爲了獲取更多的記錄
SQL> insert into heap_table select e_id,'work','123 mian street','shanghai','CC','1001' from emp;
65581 rows created.
SQL> insert into heap_table select e_id,'home','123 mian street','shanghai','CC','1001' from emp;
SQL> select count(*) from heap_table;
COUNT(*)
----------
262324
最後大概有三四十W條數據就行了,
SQL> insert into iot_address select e_id,'bee','123 mian street','shanghai','CC','1001' from emp;
SQL> select count(*) from iot_address;
COUNT(*)
----------
262324
開啓跟蹤的命令: set autotrace traceonly;
select * from biao.emp e,biao.heap_table b where e.e_id=b.empno and e.e_id=200;
| 0 | SELECT STATEMENT | | 4 | 396 | 4 (0
)| 00:00:01 |
| 1 | NESTED LOOPS | | 4 | 396 | 4 (0
)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 44 | 2 (0
)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | EMP_PK | 1 | | 1 (0
)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| HEAP_TABLE | 4 | 220 | 2 (0
)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | SYS_C0010824 | 4 | | 1 (0
)| 00:00:01 |
--------------------------------------------------------------------------------
-------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("E"."E_ID"=200)
5 - access("B"."EMPNO"=200)
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
11 consistent gets
0 physical reads
0 redo size
1352 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
4 rows processed
然後看emp和iot_address的結合讀
select * from biao.emp e,biao.iot_address b where e.e_id=b.empno and e.e_id=200;
| 0 | SELECT STATEMENT | | 46 | 4554 | 19
(0)| 00:00:01 |
| 1 | NESTED LOOPS | | 46 | 4554 | 19
(0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 44 | 2
(0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | EMP_PK | 1 | | 1
(0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | SYS_IOT_TOP_73584 | 46 | 2530 | 17
(0)| 00:00:01 |
--------------------------------------------------------------------------------
------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("E"."E_ID"=200)
4 - access("B"."EMPNO"=200)
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
1352 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
4 rows processed
關於統計信息的解釋:
db block gets : 從buffer cache中讀取的block的數量
consistent gets: 從buffer cache中讀取的undo數據的block的數量
physical reads: 從磁盤讀取的block的數量
redo size: DML生成的redo的大小
sorts (memory) :在內存執行的排序量
sorts (disk) :在磁盤上執行的排序量
Physical Reads通常是我們最關心的,如果這個值很高,
說明要從磁盤請求大量的數據到Buffer Cache裏,通常意味着系統裏存在大量全表掃描的SQL語句,這會影響到數據庫的性能,
因此儘量避免語句做全表掃描,對於全表掃描的SQL語句,建議增 加相關的索引,優化SQL語句來解決。
通過上面兩個完全相同的SQL語句,對比兩個的執行計劃,發現
對於子表是普通表的heap_table,需要經過五個步驟,首先通過EMP_PK 主鍵先找到主表emp中E_ID只爲200的行,然後通過主表字段e_id 來訪問子表heap_table,再通過子表SYS_C0010824 獲取子表記錄,整個過程需要11次內存讀
對於子表是IOT的iot_adress EMP_PK這個主鍵來訪問主表EMP,得到主表的行記錄,然後直接通過EMPNO字段來訪問子表IOT_ADDRESS,
而EMPNO字段同時也是子表IOT_ADDRESS的主鍵字段,這樣通過IOT表的主鍵字段來訪問數據就非常快了。整個SQL耗費7次內存讀
剛纔的兩個SQL是對比二者的讀取速度來的,接着看下佔用的空間
首先對兩個表進行解析:
SQL> exec dbms_stats.gather_table_stats('BIAO','HEAP_TABLE');
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats('BIAO','IOT_ADDRESS');
PL/SQL procedure successfully completed.
SQL> select num_rows,blocks,empty_blocks as empty,avg_space,avg_row_len from dba_tables where table_name='HEAP_TABLE';
NUM_ROWS BLOCKS EMPTY AVG_SPACE AVG_ROW_LEN
---------- ---------- ---------- ---------- -----------
262324 1756 0 0 42
佔用1756個塊,一個塊的大小是8k,1756 * 8 有14M之多
因爲IOT表是不佔用空間段的,是存放在索引塊裏面的,通過上面的命令是查找不出來的
exec dbms_stats.gather_index_stats(OWNER,INDEX_NAME)
而IOT的索引是表的主鍵約束名,如果你指定了約束名,就可以直接analyze分析主鍵約束名就可以了:
analyze index SYS_IOT_TOP_73584(主鍵約束名) validate structure;
而像我們的列子,是沒有指定約束名的,就只有通過以下語句進行查看約束名:
查看基於表和OWNER的約束名:
SELECT constraint_name, table_name, r_owner, r_constraint_name
FROM all_constraints where table_name='IOT_ADDRESS' and owner='BIAO';
SQL> select lf_blks, br_blks, used_space/1024/1024 MB,opt_cmpr_count, opt_cmpr_pctsave from index_stats;
LF_BLKS BR_BLKS MB OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
---------- ---------- ---------- -------------- ----------------
4013 12 12.3336878 1 4
LF_BLKS 是葉塊,BR_BLKS 是枝塊 佔用的空間是12M多,OPT_CMPR_COUNT是代表可以被壓縮的值
總結下:IOT表和堆表的優勢:
快速的主鍵訪問數據(如果不是依據主鍵查詢數據,不適合IOT表)
節省存儲空間
有以下的限制:
必須有一個主鍵,而且主鍵作爲查詢的條件(where)子句中
不能使用唯一性約束
不能使用簇
結合以上兩點,IOT使用的環境
應用中完全有 由主鍵構成的表
應用中有隻有 由主鍵來訪問數據中的表
希望數據以某種特定順序存儲的
參考資料:http://blog.csdn.net/tianlesoftware/article/details/5827245
URL 可以更多的瞭解和學習統計信息
五:IOT表壓縮
剛纔我們有通過analyze 對IOT進行分析,OPT_CMPR_COUINT表示最優壓縮數,使用compress 1 來重建IOT表
SQL> alter table iot_address move compress 1;(重建)
Table altered
select lf_blks, br_blks, used_space/1024/1024 MB,opt_cmpr_count, opt_cmpr_pctsave from index_stats;
無數據,再次進行分析
SQL> analyze index SYS_IOT_TOP_73584 validate structure; 約束名沒有變的
select lf_blks, br_blks, used_space/1024/1024 MB,opt_cmpr_count, opt_cmpr_pctsave from index_stats;
LF_BLKS BR_BLKS MB OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
---------- ---------- ---------- -------------- ----------------
1725 4 11.7874393 1 0
現在發現葉子塊有1725個,枝塊4個,佔用空間11.78MB
因爲分析出來的最優壓縮爲1,如果你想指定壓縮數爲2
會提示你:
SQL> alter table iot_address move compress 2;
alter table iot_address move compress 2
*
ERROR at line 1:
ORA-25194: invalid COMPRESS prefix length value
說明:
使用不同的壓縮級別,索引的佔用的空間大小逐漸變小
OPT_CMPR_COUNT字段表示最優壓縮數,對於該索引來說爲1是最優的,超過這個值,會提示你ORA-25194 error message
OPT_CMPR_PCTSAVE字段值則表示最優壓縮節省的空間百分比,也就是說使用壓縮值爲1後,大概能節約4%的空間
關於IOT表的學習,先到這,後續會再學習相關的知識,以上總結大都來自網絡,oracle QQ交流羣:329638713 新手學習,互幫互助!