天天看點

Oracle之虛拟索引

<b>Oracle之虛拟索引</b>

Oracle之虛拟索引

各位技術愛好者,看完本文後,你可以掌握如下的技能,也可以學到一些其它你所不知道的知識,~O(∩_∩)O~:

① Oracle虛拟索引的使用

<b>  Tips:</b><b></b>

① 本文在ITpub

④ 本篇BLOG中指令的輸出部分需要特别關注的地方我都用灰色背景和粉紅色字型來表示,比如下邊的例子中,thread 1的最大歸檔日志号為33,thread 2的最大歸檔日志号為43是需要特别關注的地方;而指令一般使用黃色背景和紅色字型标注;對代碼或代碼輸出部分的注釋一般采用藍色字型表示。

  List of Archived Logs in backup set 11

  Thrd Seq     Low SCN    Low Time            Next SCN   Next Time

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

  1    32      1621589    2015-05-29 11:09:52 1625242    2015-05-29 11:15:48

  1    33      1625242    2015-05-29 11:15:48 1625293    2015-05-29 11:15:58

  2    42      1613951    2015-05-29 10:41:18 1625245    2015-05-29 11:15:49

  2    43      1625245    2015-05-29 11:15:49 1625253    2015-05-29 11:15:53

[ZHLHRDB1:root]:/&gt;lsvg -o

T_XDESK_APP1_vg

rootvg

[ZHLHRDB1:root]:/&gt;

00:27:22 SQL&gt; alter tablespace idxtbs read write;

====》2097152*512/1024/1024/1024=1G 

<b>本文如有錯誤或不完善的地方請大家多多指正,ITPUB留言或QQ皆可,您的批評指正是我寫作的最大動力。</b><b></b>

傳統的性能優化和調整工作,大都是在系統上線之後,由運維團隊進行的。當系統資料量積累到一定程度之後,原有一些隐藏的問題就不斷出現。是以,在大資料量、應急場景下進行SQL調優,往往是運維團隊經常遇到的問題。添加索引是我們經常使用的性能優化手段。在遇到問題的時候,試一試添加索引,看看能不能改變執行計劃,是我們分析和解決問題的過程手段。但是對于大資料表情況下,快速的建立索引是比較困難的事情。這個時候,我們可以利用Oracle的virtual index技術。

在資料庫優化中,索引的重要性不言而喻。但是,在性能調整過程中,一個索引是否能被查詢用到,在索引建立之前是無法确定的,而建立索引是一個代價比較高的操作,尤其是資料量較大的時候。

虛拟索引(Virtual Index)是定義在資料字典中的僞索引,但沒有相關的索引段。虛拟索引的目的是模拟索引的存在而不用真實的建立一個完整索引。這允許開發者建立虛拟索引來檢視相關執行計劃而不用等到真實建立完索引才能檢視索引對執行計劃的影響,并且不會增加存儲空間的使用。如果我們觀察到優化器生成了一個昂貴的執行計劃并且SQL調整指導建議我們對某些的某列建立索引,但在生産資料庫環境中建立索引與測試并不總是可以操作。我們需要確定建立的索引将不會對資料庫中的其它查詢産生負面影響,是以可以使用虛拟索引。

虛拟索引不是實體存在的,它并不會建立實際的索引段,隻是在資料字典中加了一個索引的記錄,使得優化器能夠意識到一個索引的存在,進而判斷是否使用該索引作為通路路徑。當然,實際上最終查詢的通路路徑是不會使用該虛拟索引的。是以,虛拟索引的用處就是用來判斷一個索引對于SQL的執行計劃的影響,尤其是對整個資料庫的影響,進而判斷是否需要建立實體索引。可以将這個索引删掉并重建正常索引。

虛拟索引與不可見索引的不同之處在于不可見索引是有與之相關的存儲的,隻是優化器不能選擇它們。而虛拟索引沒有與之相關的存儲空間。由于這個原因,虛拟索引也被稱為無段索引。

Oracle文檔中并沒有提到虛拟索引的建立文法,實際上就是普通索引文法後面加一個NOSEGMENT關鍵字即可,B*TREE INDEX和BITMAP INDEX都可以。

必須設定隐含參數"_USE_NOSEGMENT_INDEXES"=TRUE(預設為FALSE)後CBO優化器模式才能使用虛拟索引,RBO優化器模式無法使用虛拟索引。

--建立虛拟索引,首先要将_use_nosegment_indexes的隐含參數設定為true

SQL&gt; alter session set "_use_nosegment_indexes"=true;

--虛拟索引的建立文法比較簡單,實際上就是普通索引文法後面加一個nosegment關鍵字

SQL&gt; create index ix_t_id on t(object_id) nosegment;

索引已建立。

Oracle Virtual Index是一個研究工具,是我們在投産環境上繼續SQL優化方案研究時候的不錯工具。它既滿足了讓我們建立索引,看執行計劃效果的需求。同時也不會消耗很多的索引build資源。

查找系統中已經存在的虛拟索引:

SELECT INDEX_OWNER<b>,</b> INDEX_NAME

  FROM DBA_IND_COLUMNS

 WHERE INDEX_NAME NOT LIKE 'BIN$%'

MINUS

SELECT OWNER<b>,</b> INDEX_NAME

  FROM DBA_INDEXES<b>;</b>

虛拟索引支援B-TREE索引和BIT位圖索引,在CBO模式下ORACLE優化器會考慮虛拟索引,但是在RBO模式下需要添加hint才行。

同樣的資料量,若使用nosegment虛拟索引使用的時間很短。

1. 虛拟索引無法執行alter index選項

SQL&gt; alter index IX_T_ID rebuild;

alter index IX_T_ID rebuild*

第 1 行出現錯誤:

ORA-08114: 無法變更假索引

2. 使用資源回收筒特性的時候,虛拟索引必須顯示drop,才能建立同名的索引。

SQL&gt; create index ind_status on t(status);

SQL&gt; drop table t;

表已删除。

SQL&gt; flashback table t to before drop;

閃回完成。

SQL&gt; select table_name,index_name,status from user_indexes where table_name='T';

TABLE_NAME               INDEX_NAME              STATUS

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

T                   BIN7jAFlUG6b1zgQAB/AQAPyw==7jAFlUG6b1zgQAB/AQAPyw==0 VALID

SQL&gt; create index ind_object_id on t(object_id);

SQL&gt; create index inds_status on t(status);create index inds_status on t(status)

                              *

ORA-01408: 此列清單已索引

3. 不能建立和虛拟索引同名的實際索引;

4. 可以建立和虛拟索引包含相同列但不同名的實際索引;

5. 虛拟索引分析并且有效,但是資料字典裡查不到結果。

SYS@lhrdb&gt; select * from v$version;

BANNER

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

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

PL/SQL Release 11.2.0.4.0 - Production

CORE    11.2.0.4.0      Production

TNS for IBM/AIX RISC System/6000: Version 11.2.0.4.0 - Production

NLSRTL Version 11.2.0.4.0 - Production

SYS@lhrdb&gt; CREATE TABLE T_VI_20160818_01_LHR AS SELECT * FROM DBA_OBJECTS;

Table created.

虛拟索引的建立文法比較簡單,實際上就是普通索引文法後面加一個nosegment關鍵字

SYS@lhrdb&gt; CREATE INDEX IX_VI01_ID ON T_VI_20160818_01_LHR(OBJECT_ID) NOSEGMENT;

Index created.

從資料字段中是無法找到這個索引的。

SYS@lhrdb&gt; SELECT INDEX_NAME,STATUS FROM DBA_INDEXES WHERE TABLE_NAME='T_VI_20160818_01_LHR';

no rows selected

SYS@lhrdb&gt; COL OBJECT_NAME FORMAT A10

SYS@lhrdb&gt; SELECT D.OWNER,D.OBJECT_NAME,D.OBJECT_TYPE FROM DBA_OBJECTS D WHERE D.OBJECT_NAME='IX_VI01_ID';

OWNER                          OBJECT_NAM OBJECT_TYPE

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

SYS                            IX_VI01_ID INDEX

SYS@lhrdb&gt; SELECT TO_CHAR(DBMS_METADATA.GET_DDL('INDEX','IX_VI01_ID')) FROM DUAL;

TO_CHAR(DBMS_METADATA.GET_DDL('INDEX','IX_VI01_ID'))

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

  CREATE INDEX "SYS"."IX_VI01_ID" ON "SYS"."T_VI_20160818_01_LHR" ("OBJECT_ID")

  PCTFREE 10 INITRANS 2 MAXTRANS 255  NOSEGMENT

使用虛拟索引,首先要将_use_nosegment_indexes的隐含參數設定為true

SYS@lhrdb&gt; ALTER SESSION SET "_USE_NOSEGMENT_INDEXES"=TRUE;

Session altered.

SYS@lhrdb&gt; SHOW PARAMETER optimizer_mode

NAME                                 TYPE        VALUE

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

optimizer_mode                       string      ALL_ROWS

SYS@lhrdb&gt; SET AUTOTRACE TRACEONLY

SYS@lhrdb&gt; SET LINE 9999

SYS@lhrdb&gt; SELECT * FROM T_VI_20160818_01_LHR WHERE OBJECT_ID=1;

Execution Plan

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

Plan hash value: 3209519479

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

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

|   0 | SELECT STATEMENT            |                      |    14 |  2898 |     5   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| T_VI_20160818_01_LHR |    14 |  2898 |     5   (0)| 00:00:01 |

|*  2 |   INDEX RANGE SCAN          | IX_VI01_ID           |   312 |       |     1   (0)| 00:00:01 |

Predicate Information (identified by operation id):

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

   2 - access("OBJECT_ID"=1)

Note

-----

   - dynamic sampling used for this statement (level=2)

Statistics

          0  recursive calls

          0  db block gets

       1249  consistent gets

          0  physical reads

          0  redo size

       1343  bytes sent via SQL*Net to client

        509  bytes received via SQL*Net from client

          1  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          0  rows processed

SYS@lhrdb&gt;

以下看的是真實執行計劃,顯然是用不到索引。

SYS@lhrdb&gt; SET AUTOTRACE OFF

SYS@lhrdb&gt; ALTER SESSION SET STATISTICS_LEVEL=ALL;

SYS@lhrdb&gt; SELECT SQL_ID,CHILD_NUMBER,SQL_TEXT FROM V$SQL WHERE SQL_TEXT LIKE '%SELECT * FROM T_VI_20160818_01_LHR WHERE OBJECT_ID=1%';

SQL_ID        CHILD_NUMBER SQL_TEXT

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

d5v59m8vyyz7d            0 SELECT * FROM T_VI_20160818_01_LHR WHERE OBJECT_ID=1

SYS@lhrdb&gt; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('d5v59m8vyyz7d',0,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT

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

SQL_ID  d5v59m8vyyz7d, child number 0

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

SELECT * FROM T_VI_20160818_01_LHR WHERE OBJECT_ID=1

Plan hash value: 847945500

| Id  | Operation         | Name                 | Starts | E-Rows | A-Rows |   A-Time   | Buffers |

|   0 | SELECT STATEMENT  |                      |      1 |        |      0 |00:00:00.01 |    1249 |

|*  1 |  TABLE ACCESS FULL| T_VI_20160818_01_LHR |      1 |     14 |      0 |00:00:00.01 |    1249 |

   1 - filter("OBJECT_ID"=1)

22 rows selected.

SYS@lhrdb&gt; SELECT INDEX_OWNER, INDEX_NAME

  2    FROM DBA_IND_COLUMNS

  3   WHERE INDEX_NAME NOT LIKE 'BIN$%'

  4  MINUS

  5  SELECT OWNER, INDEX_NAME

  6    FROM DBA_INDEXES;

INDEX_OWNER                    INDEX_NAME

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

SYS                            IX_VI01_ID

虛拟索引是定義在資料字典中的僞索引,但沒有相關的索引段。虛拟索引的目的是模拟索引的存--而不用真實的建立一個完整索引。這允許開發者建立虛拟索引來檢視相關執行計劃而不用等到真實建立完索引才能檢視索引對執行計劃的影響,并且不會增加存儲空間的使用。如果我們觀察到優化器生成了一個昂貴的執行計劃并且SQL調整指導建議我們對某些的某列建立索引,但在生産資料庫環境中建立索引與測試并不總是可以操作。我們需要確定建立的索引将不會對資料庫中的其它查詢産生負面影響,是以可以使用虛拟索引。

下面舉例進行說明

1.建立一個測試表test

2.從表test查詢object_name等于standard的記錄

3.查詢上面查詢的執行計劃

4.在表test的object_name列上建立一個虛拟索引

為了建立虛拟索引必須在create index語句中指定nosegment子句,并且不會建立索引段。

5.來驗證虛拟索引不會建立索引段

從上面的結果可以看到索引對象已經建立,但沒有建立索引段。

6.重新執行sql檢視建立的虛拟索引是否被使用

從上面的執行計劃可以清楚地看到建立的虛拟索引并沒有被使用

7.為了能使用所建立的虛拟索引,需要将_USE_NOSEGMENT_INDEXES設定為true

8.重新執行sql檢視建立的虛拟索引是否被使用

從上面的執行計劃可以看到當設定隐含參數_USE_NOSEGMENT_INDEXES後,優化器将會使用建立的虛拟索引。在使用虛拟索引需要注意,我們可以分析虛拟索引,但不能重建虛拟索引,如果重建虛拟索引會收到ORA-8114: "User attempted to alter a fake index"錯誤提示,可以删除虛拟索引。

傳統的性能優化和調整工作,大都是在系統上線之後,由運維團隊進行的。當系統資料量積累到一定程度之後,原有一些隐藏的問題就不斷出現。是以,在大資料量、應急場景下進行SQL調優,往往是運維團隊經常遇到的問題。

添加索引是我們經常使用的性能優化手段。在遇到問題的時候,試一試添加索引,看看能不能改變執行計劃,是我們分析和解決問題的過程手段。但是對于大資料表情況下,快速的建立索引是比較困難的事情。這個時候,我們可以利用Oracle的virtual index技術。

<b>1、環境介紹和資料準備</b>

Virtual Index出現的很早。筆者從9i時候的文檔資料中,就可以看到virtual index的技術材料。我們還是選擇Oracle 11gR2進行試驗。

SQL&gt; select * from v$version;

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

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

我們建立資料表T作為實驗對象,同時建立正常Index和虛拟Index。

SQL&gt; show user;

User is "scott"

SQL&gt; create table t as select * from dba_objects;

Table created

SQL&gt; set timing on;

--建立一個普通索引

SQL&gt; create index idx_t_owner on t(owner);

Index created

Executed in 0.687 seconds

SQL&gt; select count(*) from t;

  COUNT(*)

----------

     72792

Executed in 0.015 seconds

我們建立virtual index,需要使用nosegment關鍵字。

SQL&gt; create index idx_t_obj on t(object_id) nosegment;

Executed in 0.047 seconds

SQL&gt; exec dbms_stats.gather_table_stats(user,'T',cascade =&gt; true);

PL/SQL procedure successfully completed

Executed in 1.716 seconds

此處我們需要注意一個細節,同樣是在7萬多基礎資料上面建立索引。nosegment虛拟索引使用的時間很短。

<b>2、資料字典層面看virtual index</b>

我們建立了虛拟索引idx_t_obj,又建立了作為參照的idx_t_owner。下面可以從資料字典的層面,去看看虛拟索引的内容資訊。

Oracle所有索引資訊都記錄在dba_indexes視圖中。

SQL&gt; select index_name, index_type from dba_indexes where wner='SCOTT' and table_name='T';

INDEX_NAME                     INDEX_TYPE

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

IDX_T_OWNER                    NORMAL

Executed in 0.031 seconds

SQL&gt; select segment_name from dba_segments where wner='SCOTT' and segment_name in ('IDX_T_OWNER','IDX_T_OBJ');

SEGMENT_NAME

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

IDX_T_OWNER

Executed in 0.062 seconds

我們從dba_indexes和dba_segments中,都隻能看到普通索引idx_t_owner的資訊。而建立的虛拟索引idx_t_obj沒有蹤迹。nosegment選項可以讓我們猜測是沒有索引段對象的建立過程。但是,作為字典的dba_indexes資訊沒有,就讓人疑惑。

驗證我們的想法,使用dbms_metadata.get_ddl方法,抽取到資料表t的字典定義。其中,我們看到了idx_t_obj的資訊。

  CREATE INDEX "SCOTT"."IDX_T_OBJ" ON "SCOTT"."T" ("OBJECT_ID")

  PCTFREE 10 INITRANS 2 MAXTRANS 255  NOSEGMENT ;

  CREATE INDEX "SCOTT"."IDX_T_OWNER" ON "SCOTT"."T" ("OWNER")

  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS

  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645

  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)

  TABLESPACE "USERS" ;

相對于idx_t_owner,虛拟索引的定義全文顯得很簡單,隻有nosegment很顯眼。

那麼,作為萬物彙總的dba_objects中呢?

SQL&gt; select owner,object_name, object_id, data_object_id, object_type from dba_objects where object_name in ('IDX_T_OWNER','IDX_T_OBJ');

OWNER OBJECT_NAME      OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE

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

SCOTT IDX_T_OWNER          78019          78019 INDEX

SCOTT IDX_T_OBJ            78020          78020 INDEX

Executed in 0.047 seconds

在dba_objects中,我們找到idx_t_obj的資訊,它依然被認為是一個索引。更重要的是,我們定位到了object_id和data_object_id,這兩個分别為資料庫對象的邏輯id和實體段id。

dba_indexes字典視圖的基礎資料表是ind$基表。其中定義了所有索引對象的資訊。我們借助object_id去檢查,發現了無法查詢到的idx_t_obj對象記錄。

SQL&gt; select obj#, ts#, file#, block#, bo# from ind$ where obj# in (78019, 78020);

      OBJ#        TS#      FILE#     BLOCK#        BO#

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

     78019          4          4       1586      78017

     78020          4          0          0      78017

SQL&gt; select owner, object_name from dba_objects where object_id=78017;

OWNER OBJECT_NAME

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

SCOTT T

Executed in 0.016 seconds

我們可以從bo#編号,确定的确是資料表scott.t的索引對象。那麼,我們思考一個問題,既然ind$中存在對應記錄,為什麼dba_indexes不能檢索到這個資訊呢?

通過抽取dba_indexes的源碼資訊,我們可以猜到端倪。

from sys.ts$ ts, sys.seg$ s,

     sys.user$ iu, sys.obj$ io, sys.user$ u, sys.ind$ i, sys.obj$ o,

     sys.user$ itu, sys.obj$ ito, sys.deferred_stg$ ds

where u.user# = o.owner#

  and o.obj# = i.obj#

  and i.bo# = io.obj#

  and io.owner# = iu.user#

  and bitand(i.flags, 4096) = 0

  and bitand(o.flags, 128) = 0

  and i.ts# = ts.ts# (+)

  and i.file# = s.file# (+)

  and i.block# = s.block# (+)

  and i.ts# = s.ts# (+)

  and i.obj# = ds.obj# (+)

  and i.indmethod# = ito.obj# (+)

  and ito.owner# = itu.user# (+);

雖然虛拟索引是沒有段的,在seg$中必然沒有對應記錄。但是SQL語句中對于這個條件定義的是外連接配接。也就是說,即使沒有段結構,索引也能顯示出來。

疑點就落在對一些列flag标記的bitand操作上了。我們檢查一下ind$的基礎flags取值,就可以知道原因了。

SQL&gt; select obj#, bitand(flags, 4096) from ind$ where obj# in (78019, 78020);

      OBJ# BITAND(FLAGS,4096)

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

     78019                  0

     78020               4096

看來,雖然ind$中包括資訊,但是不顯示出來,也是Oracle的一個本意。

下面我們繼續來看virtual index的實際工作效果。

<b>3</b><b>、“不成功”的實驗</b>

Virtual index的特點就是沒有段segment結構的支援,在資料字典的基表中存在痕迹。那麼,它對于我們的執行計劃有什麼樣的影響呢?

這裡我們需要區分兩個概念,就是執行計劃SEP的生成和執行。Oracle優化器是一個獨立的元件,是可以單獨進行工作的。同時,Oracle執行計劃真正的情況,是從Shared Pool中抽取出來的。

Virtual index沒有segment結構支援,是以根本不可能實際去執行,即使優化器指令走virtual index路徑。那麼,我們從執行計劃和實際執行兩個角度看問題。

首先,我們不做任何額外的配置,看看在virtual index存在的情況下,預設情況下會給我們帶來什麼。

--反映Oracle Optimizer的判定;

SQL&gt; explain plan for select * from t where object_id = 10000;

Explained

SQL&gt; select * from table(dbms_xplan.display);

Plan hash value: 1601196873

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

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

|   0 | SELECT STATEMENT  |      |     1 |    97 |   273   (1)| 00:00:04 |

|*  1 |  TABLE ACCESS FULL| T    |     1 |    97 |   273   (1)| 00:00:04 |

   1 - filter("OBJECT_ID"=10000)

13 rows selected

Executed in 0.109 seconds

Explain plan for是優化器單獨工作,SQL是不真正執行的!看來virtual index不會在這個時候影響優化器。

那麼,運作時如何?我們先執行SQL,從shared pool中抽取sql_id資訊。

SQL&gt; select /*+demo*/count(*) from t where object_id=10000;

         1

Executed in 0.078 seconds

SQL&gt; select sql_id, executions, version_count from v$sqlarea where sql_text like 'select /*+demo*/count(*)%';

SQL_ID        EXECUTIONS VERSION_COUNT

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

d2s9wnt37f4g7          1             1

利用dbms_xplan包進行抽取。

SQL&gt; select * from table(dbms_xplan.display_cursor(sql_id =&gt; 'd2s9wnt37f4g7'));

SQL_ID  d2s9wnt37f4g7, child number 0

select /*+demo*/count(*) from t where object_id=10000

Plan hash value: 2966233522

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

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

|   0 | SELECT STATEMENT   |      |       |       |   273 (100)|          |

|   1 |  SORT AGGREGATE    |      |     1 |     5 |            |          |

|*  2 |   TABLE ACCESS FULL| T    |     1 |     5 |   273   (1)| 00:00:04 |

   2 - filter("OBJECT_ID"=10000)

19 rows selected

實際執行的SQL中,也沒有執行virtual index路徑。是以:在預設的情況下,virtual index既不會參與單獨的Optimizer決定,也不會生成與virtual index有關的真實執行計劃來執行。

<b>4</b><b>、“受到影響”的優化器</b>

要讓virtual index起作用,需要調整一個Oracle隐含參數_use_nosegment_indexes。預設這個參數取值為false,表示不開啟nosegment indexes功能。

我們可以在instance和session兩個level去設定這個參數。

SQL&gt; alter session set "_use_nosegment_indexes" = true;

Session altered

Executed in 0 seconds

我們再來看剛剛的實驗。

--explain plan for指令

SQL&gt; explain plan for select * from t where object_id = 10000;

Plan hash value: 2999300365

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

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

|   1 |  TABLE ACCESS BY INDEX ROWID| T         |     1 |    97 |     2   (0)| 0

|*  2 |   INDEX RANGE SCAN          | IDX_T_OBJ |     1 |       |     1   (0)| 0

   2 - access("OBJECT_ID"=10000)

14 rows selected

我們發現,單獨調用optimizer工作的時候,virtual index路徑被走到了。那麼,真實執行呢?

--真正去執行一下

SQL&gt; select /*+demo_2*/count(*) from t where object_id=10000;

SQL&gt; select sql_id, executions, version_count from v$sqlarea where sql_text like 'select /*+demo_2*/count(*)%';

8gbx9grs6cga2          1             1

SQL&gt; select * from table(dbms_xplan.display_cursor(sql_id =&gt; '8gbx9grs6cga2'));

SQL_ID  8gbx9grs6cga2, child number 0

select /*+demo_2*/count(*) from t where object_id=10000

從實際情況看,設定了隐含參數後,單獨CBO進行執行計劃判定的時候,是會考慮nosegment索引的。但是,在真正執行的時候,還是不會考慮virtual index,因為這個索引并不存在,也不能支援真正執行。

<b>5</b><b>、CBO or RBO</b>

此時,筆者想到一個問題。Oracle CBO在工作的時候,索引路徑隻是執行計劃的一種“可選路徑”。究竟是FTS(Full Table Scan)還是Index Path,取決于統計量計算出的成本值。

那麼,virtual index在工作的時候,沒有段結構與之對應,統計量也必然有一些不完全。那麼,Oracle在生成執行計劃的時候,是否進行CBO判定呢?

一個最簡單的方法,就是偏移列索引路徑判定。

--構造偏移列

SQL&gt; update t set wner='SYS' where owner &lt;&gt; 'SCOTT';

72773 rows updated

Executed in 7.628 seconds

SQL&gt; commit;

Commit complete

删除原有的owner列一般索引,建立nosegment索引。

SQL&gt; drop index idx_t_owner;

Index dropped

Executed in 0.234 seconds

SQL&gt; create index idx_t_owner on t(owner) nosegment;

Executed in 1.123 seconds

--不存在index正式結論;

SQL&gt; select count(*) from dba_indexes where wner='SCOTT' and index_name='IDX_T_OWNRE';

測試對owner列的選擇執行計劃。

SQL&gt; alter session set "_use_nosegment_indexes" = true;

--小數值執行計劃

SQL&gt; explain plan for select * from t where wner='SCOTT';

Plan hash value: 1516787156

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

|   0 | SELECT STATEMENT            |             |    13 |  1235 |     2   (0)|

|   1 |  TABLE ACCESS BY INDEX ROWID| T           |    13 |  1235 |     2   (0)|

|*  2 |   INDEX RANGE SCAN          | IDX_T_OWNER |    13 |       |     1   (0)|

   2 - access("OWNER"='SCOTT')

--大數值偏移執行計劃

SQL&gt; explain plan for select * from t where wner='SYS';

|   0 | SELECT STATEMENT  |      | 72772 |  6751K|   273   (1)| 00:00:04 |

|*  1 |  TABLE ACCESS FULL| T    | 72772 |  6751K|   273   (1)| 00:00:04 |

   1 - filter("OWNER"='SYS')

Executed in 0.063 seconds

看來,在工作中的确是CBO成本運算。對于一些不存在的統計值,Oracle可能是選擇一個預設值來定義計算。

<b>6</b><b>、結論</b>

About Me ............................................................................................................................... ● 本文作者:小麥苗,隻專注于資料庫的技術,更注重技術的運用 ● QQ群:230161599     微信群:私聊 ● 聯系我請加QQ好友(646634621),注明添加緣由 ● 于 2017-05-09 09:00 ~ 2017-05-30 22:00 在魔都完成 ● 文章内容來源于小麥苗的學習筆記,部分整理自網絡,若有侵權或不當之處還請諒解 ● 版權所有,歡迎分享本文,轉載請保留出處 拿起手機使用微信用戶端掃描下邊的左邊圖檔來關注小麥苗的微信公衆号:xiaomaimiaolhr,掃描右邊的二維碼加入小麥苗的QQ群,學習最實用的資料庫技術。
Oracle之虛拟索引
Oracle之虛拟索引
<a target="_blank" href="http://wpa.qq.com/msgrd?v=3&amp;uin=646634621&amp;site=qq&amp;menu=yes"></a>
Oracle之虛拟索引
Oracle之虛拟索引
Oracle之虛拟索引