唯一性索引(Unique Index)與普通索引(Normal Index)差異(上)
索引是我們經常使用的一種資料庫搜尋最佳化手段。適當的業務操作場景使用適當的索引方案可以顯著的提升系統整體效能和使用者體驗。在Oracle中,索引有包括很多型別。不同型別的索引適應不同的系統環境和訪問場景。其中,唯一性索引Unique Index是我們經常使用到的一種。
唯一性索引unique index和一般索引normal index最大的差異就是在索引列上增加了一層唯一約束。新增唯一性索引的資料列可以為空,但是隻要存在資料值,就必須是唯一的。
那麼,在使用唯一性索引時,同一般索引有什麼差異呢?下面透過一系列的演示實驗來說明。
1、實驗環境準備
為了體現出一致性和可能的差異性,選擇相同的資料值列加入不同型別的索引結構。
SQL> select * from v$version where rownum<2;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
SQL> create table t as select * from dba_objects;
Table created
//保證data_object_id和object_id取值相同;
SQL> update t set data_object_id=object_id;
72581 rows updated
SQL> commit;
Commit complete
//普通索引
SQL> create index idx_t_normalid on t(object_id);
Index created
//唯一性索引
SQL> create unique index idx_t_uniid on t(data_object_id);
Index created
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
2、體積容量比較
在環境準備中,我們將索引列取值設定為完全相同,儘量避免由於外在原因引起的差異。下面我們檢查資料字典中的容量比較資訊。
首先是檢視索引段index segment資訊。
SQL> select segment_name, segment_type, bytes, blocks, extents from dba_segments where segment_name in ('IDX_T_NORMALID','IDX_T_UNIID');
SEGMENT_NAME SEGMENT_TYPE BYTES BLOCKS EXTENTS
-------------------- ------------------ ---------- ---------- ----------
IDX_T_NORMALID INDEX 983040 120 15
IDX_T_UNIID INDEX 917504 112 14
一般索引normal index較唯一性索引空間消耗略大。索引idx_t_normalid佔據15個分割槽,120個block。略大於idx_t_uniid的14個分割槽塊。
這個地方需要注意一下,在資料字典中一個segment的分割槽佔據,是Oracle系統分配給的空間,並不意味著全部使用殆盡。可能兩個索引結構差異很小,但是額外多分配出一個extent。
索引葉子結構上,檢查資料字典內容。
SQL> select index_name, index_type, UNIQUENESS, BLEVEL, LEAF_BLOCKS, DISTINCT_KEYS from dba_indexes where index_name in ('IDX_T_NORMALID','IDX_T_UNIID');
INDEX_NAME INDEX_TYPE UNIQUENESS BLEVEL LEAF_BLOCKS DISTINCT_KEYS
-------------------- --------------- ---------- ---------- ----------- -------------
IDX_T_UNIID NORMAL UNIQUE 1 106 51330
IDX_T_NORMALID NORMAL NONUNIQUE 1 113 51330
兩者的差異不大,normal index空間消耗要略大於unique index。
結論:從資料字典反映出的情況可以知道,同一般索引相比,唯一性索引的空間消耗略小一些。由於我們採用的實驗資料都是相同的,這一點點的差距可能就意味著兩種索引型別在儲存結構上存在略微的差異。
3、違反約束實驗
作為唯一性索引,在新增建立和進行dml操作的時候,都會嚴格發揮出約束的作用。
SQL> insert into t select * from t where rownum<2;
insert into t select * from t where rownum<2
ORA-00001: 違反唯一約束條件 (SYS.IDX_T_UNIID)
4、等號檢索實驗
當我們進行等號查詢的時候,Oracle對兩種索引生成的執行計劃有何種差異?注意:下面的select檢索之前,都使用flush語句對shared_pool和buffer_cache進行清理。
--精確查詢
SQL> select * from t where object_id=1000;
執行計劃
----------------------------------------------------------
Plan hash value: 776407697
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 101 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 101 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_T_NORMALID | 1 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID"=1000)
統計資訊
----------------------------------------------------------
528 recursive calls
0 db block gets
87 consistent gets
11 physical reads
0 redo size
1200 bytes sent via SQL*Net to client
376 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select * from t where data_object_id=1000;
執行計劃
----------------------------------------------------------
Plan hash value: 335537167
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 101 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 101 | 2 (0)| 00:00:01 |
|* 2 | INDEX UNIQUE SCAN | IDX_T_UNIID | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DATA_OBJECT_ID"=1000)
統計資訊
----------------------------------------------------------
528 recursive calls
0 db block gets
86 consistent gets
10 physical reads
0 redo size
1200 bytes sent via SQL*Net to client
376 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
1 rows processed
這裡,我們看到了Unique Index的一個特性,就是等號操作時執行計劃的差異。對Unique Index而言,進行相等查詢的結果只有一行值或者沒有,所以沒必要進行傳統的Index Range Scan操作。此處,執行計劃中使用的是Index Unique Scan操作,直接精確定位到指定的記錄專案,返回rowid記錄。
而一般索引在進行等號檢索的時候,通常使用的就是Index Range Scan操作。沿著索引樹葉子節點進行水平掃描操作,直到獲取索引符合條件索引列值的rowid列表。
從成本上看,兩者雖然執行計劃操作方式有一定差別,但是成本實際差異不大。CPU成本和執行時間上相同。各種塊讀操作(邏輯讀和物理讀)存在一些差異,筆者認為源於兩個索引結構的微量區別,這樣讀取的塊數一定有些差異。
5、範圍搜尋實驗
當我們進行索引列的範圍搜尋時,執行計劃和成本有何種差異呢?
--範圍匹配
SQL> select * from t where object_id>=1000 and object_id<=1500;
已選擇490行。
執行計劃
----------------------------------------------------------
Plan hash value: 776407697
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 485 | 48985 | 14 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 485 | 48985 | 14 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_T_NORMALID | 485 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID">=1000 AND "OBJECT_ID"<=1500)
統計資訊
----------------------------------------------------------
528 recursive calls
0 db block gets
158 consistent gets
17 physical reads
0 redo size
23775 bytes sent via SQL*Net to client
728 bytes received via SQL*Net from client
34 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
490 rows processed
SQL> select * from t where data_object_id>=1000 and data_object_id<=1500;
已選擇490行。
執行計劃
----------------------------------------------------------
Plan hash value: 2700411221
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 485 | 48985 | 14 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 485 | 48985 | 14 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_T_UNIID | 485 | | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("DATA_OBJECT_ID">=1000 AND "DATA_OBJECT_ID"<=1500)
統計資訊
----------------------------------------------------------
528 recursive calls
0 db block gets
157 consistent gets
16 physical reads
0 redo size
23775 bytes sent via SQL*Net to client
728 bytes received via SQL*Net from client
34 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
490 rows processed
從實驗結果看,兩者在進行範圍搜尋時,執行計劃沒有差異。兩者都是進行Index Range Scan操作。各型別成本基本相同。
6、結論
本篇主要從應用角度,分析Unique Index與一般normal Index的差異。從結果看,Unique Index就是額外新增的唯一性約束。該約束嚴格的保證索引列的取值唯一性,這在一些資料列上的業務約束實現是很重要的功能。比如一個資料列,不能作為主鍵,而且允許為空,但是業務上要求唯一特性。這時候使用唯一性索引就是最好的選擇。
從執行計劃where條件中的表現看,Unique Index和一般normal Index沒有顯著性的差異。
兩者資料基礎值一樣的情況下,生成索引的體積存在略微的差異,說明在儲存結構上兩者可能有不同。下面我們來分析一下兩型別索引的結構資訊。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/17203031/viewspace-700089/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 唯一性索引(Unique Index)與普通索引(Normal Index)差異(中)索引IndexORM
- 唯一性索引(Unique Index)與普通索引(Normal Index)差異(下)索引IndexORM
- Unique Index和Normal Index差異經典對比IndexORM
- 再說Unique Index和Normal Index行為差異IndexORM
- 索引唯一性掃描(INDEX UNIQUE SCAN)索引Index
- Elasticsearch之索引模板index template與索引別名index aliasElasticsearch索引Index
- index索引Index索引
- oracle dml與索引index(一)Oracle索引Index
- 主鍵(PK)與非空唯一索引(Unique Index)的區別索引Index
- 分割槽索引之本地(local index)索引和全域性索引(global index)索引Index
- mysql索引型別:FULLTEXT、NORMAL、SPATIAL、UNIQUEMySql索引型別ORM
- Mysql——index(索引)使用MySqlIndex索引
- oracle index索引原理OracleIndex索引
- mysql 索引( mysql index )MySql索引Index
- Index Full Scan和Index Fast Full Scan行為差異分析(上)IndexAST
- 點陣圖索引(Bitmap Index)——索引共用索引Index
- 【INDEX】Postgresql索引介紹IndexSQL索引
- pandas(3):索引Index/MultiIndex索引Index
- 分割槽索引(Partition Index)與SQL執行計劃(上)索引IndexSQL
- 介紹Oracle Virtual Index虛擬索引(上)OracleIndex索引
- create index , rebuild index troubleshooting 索引故障解決IndexRebuild索引
- pk 、unique index 和 index 區別Index
- 聊聊索引Index Rebuild和Rebuild Online(上)索引IndexRebuild
- 分析index降低索引層次Index索引
- index merge合併索引Index索引
- Oracle索引分裂(Index Block Split)Oracle索引IndexBloC
- 索引優化index skip scan索引優化Index
- Oracle筆記 之 索引(index)Oracle筆記索引Index
- oracle index索引結構(一)OracleIndex索引
- ElasticSearch 倒排索引(Inverted Index)| 什麼是倒排索引?Elasticsearch索引Index
- 【INDEX】使用“alter index ××× monitoring usage;”語句監控索引使用與否Index索引
- INDEX UNIQUE SCAN,INDEX FULL SCAN和INDEX FAST FULL SCANIndexAST
- Index Unique Scan (213)Index
- oracle全文索引之STOPLIST_ CTXCAT 索引_INDEX SETOracle索引Index
- oracle 點陣圖索引(bitmap index)Oracle索引Index
- 點陣圖索引:原理(BitMap index)索引Index
- zt_如何加速索引index建立索引Index
- 使用Index提示 強制使用索引Index索引