1. 程式人生 > >【Oracle index】組合索引如何選擇前導列的幾點考慮

【Oracle index】組合索引如何選擇前導列的幾點考慮

選擇組合索引的前導列,必須根據具體的業務(SQL)寫法和列的資料分佈不同而不同,很多書或網上都說,前導列要選擇高選擇性的,但是,脫離具體的業務,這些是沒有意義的,本文就舉一些常見的例子來分析下如何正確選擇前導列,以拋磚引玉,實際應用中,有更多複雜的情況需要具體分析。

1.都是等值條件的列,誰做前導列都一樣

DROP TABLE t;
CREATE TABLE t 
AS
SELECT * FROM dba_objects;
CREATE INDEX idx1_t ON t(owner,object_id);
CREATE INDEX idx2_t ON t(object_id,owner);
BEGIN
  dbms_stats.gather_table_stats(ownname => USER,tabname => 'T',estimate_percent => 100,cascade => TRUE);

END;
 /
[email protected]>  SELECT COUNT(DISTINCT owner),COUNT(DISTINCT object_id),COUNT(*) FROM t;
COUNT(DISTINCTOWNER) COUNT(DISTINCTOBJECT_ID)   COUNT(*)
-------------------- ------------------------ ----------
                  33                    75250      75251
1 row selected.

owner有33個不同的值,object_id有75250,顯然object_id的選擇性更好。但是下面的查詢,應用idx1_t與idx2_t的效能一樣(COST與CONSISTENT GETS一樣)。


[email protected]>  SELECT/*+index(t idx1_t)*/ * FROM t
  2   WHERE owner='DINGJUN123' AND object_id=75677;
1 row selected.
Elapsed: 00:00:00.00
Execution Plan
----------------------------------------------------------
Plan hash value: 2071967826
--------------------------------------------------------------------------------------

| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |    97 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |     1 |    97 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX1_T |     1 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OWNER"='DINGJUN123' AND "OBJECT_ID"=75677)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1403  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed


[email protected]>   SELECT/*+index(t idx2_t)*/ * FROM t
  2   WHERE owner='DINGJUN123' AND object_id=75677;
1 row selected.
Elapsed: 00:00:00.01
Execution Plan
----------------------------------------------------------
Plan hash value: 3787301248
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |    97 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |     1 |    97 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX2_T |     1 |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID"=75677 AND "OWNER"='DINGJUN123')
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1403  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed


      雖然如此,但是要記住,這個索引不是僅僅給這2條SQL使用的,事實上可能我們有的查詢謂詞只有owner或object_id,這時候得考慮使用owner作為前導列還是使用object_id作為前導列。
    還有其他引用owner,object_id的情況,比如GROUP BY ,ORDER BY,甚至SELECT...都需要進行整體的分析,這樣才能建立最佳的索引。
 
2.有的列是大於(等於)或小於(等於)或者是like 模糊匹配等不等條件,有的列是等值的條件,等值的一般作為前導列更好
--做5次,增加幾十萬行SYS的進去

INSERT INTO t SELECT * FROM t WHERE owner='SYS';
COMMIT;
--重新收集統計資訊(省略)
[email protected]> SELECT * FROM t
  2  WHERE owner='DINGJUN123'
  3  AND object_id>=107889;
1 row selected.
Elapsed: 00:00:00.01
Execution Plan
----------------------------------------------------------
Plan hash value: 2071967826
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |    96 |     4   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |     1 |    96 |     4   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX1_T |     1 |       |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OWNER"='DINGJUN123' AND "OBJECT_ID">=107889 AND "OBJECT_ID" IS
              NOT NULL)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          5  consistent gets
          0  physical reads
          0  redo size
       1399  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

      上面的SQL走idx1_t,注意觀察謂詞,只有access,說明索引完全被利用上,很顯然因為owner是前導列,而且是等值查詢,按照前導列查詢,然後只要分析索引的第2列object_id,當發現不滿足條件object_id>=107889之後就停止了,索引掃描沒有浪費。

[email protected]> SELECT/*+index(t idx2_t)*/ * FROM t
  2  WHERE owner='DINGJUN123'
  3  AND object_id>=107889;
1 row selected.
Elapsed: 00:00:00.01
Execution Plan
----------------------------------------------------------
Plan hash value: 3787301248
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |     1 |    96 |     4   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |     1 |    96 |     4   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX2_T |     1 |       |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID">=107889 AND "OWNER"='DINGJUN123' AND "OBJECT_ID" IS
              NOT NULL)
       filter("OWNER"='DINGJUN123')
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          5  consistent gets
          0  physical reads
          0  redo size
       1399  bytes sent via SQL*Net to client
        416  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

      強制使用idx2_t,object_id是前導列,謂詞有access,還有filter,說明索引沒有被完全利用上,這是因為object_id的不是等值查詢,滿足object_id>=107889的,按照順序搜尋
所以,中間可能有一些不滿足owner='DINGJUN123'的,還要filter掉。
這種查詢和不等值條件作為前導列的查詢,一旦object_id>=107889不滿足owner='DINGJUN123'的很多,那麼必然造成過多不必要的索引搜尋,COST與邏輯讀會上升很快,
從而效能急劇下降,因為本例子基本都滿足owner條件,所以沒有啥浪費。但是下面的例子

[email protected]> SELECT * FROM t
  2  WHERE owner='DINGJUN123'
  3  AND object_id>=100;
2540 rows selected.
Elapsed: 00:00:00.15
Execution Plan
----------------------------------------------------------
Plan hash value: 2071967826
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |  2539 |   238K|   499   (0)| 00:00:06 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |  2539 |   238K|   499   (0)| 00:00:06 |
|*  2 |   INDEX RANGE SCAN          | IDX1_T |  2539 |       |    12   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OWNER"='DINGJUN123' AND "OBJECT_ID">=100 AND "OBJECT_ID" IS
              NOT NULL)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        527  consistent gets
         21  physical reads
          0  redo size
     268134  bytes sent via SQL*Net to client
       2275  bytes received via SQL*Net from client
        171  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       2540  rows processed


還是使用idx1_t,沒有問題。看下面的強制使用idx2_t的。

[email protected]> SELECT/*+index(t idx2_t)*/ * FROM t
  2  WHERE owner='DINGJUN123'
  3  AND object_id>=100;

2540 rows selected.
Elapsed: 00:00:00.33
Execution Plan
----------------------------------------------------------
Plan hash value: 3787301248
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |  2539 |   238K|  3762   (1)| 00:00:46 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |  2539 |   238K|  3762   (1)| 00:00:46 |
|*  2 |   INDEX RANGE SCAN          | IDX2_T |  2539 |       |  3274   (1)| 00:00:40 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID">=100 AND "OWNER"='DINGJUN123' AND "OBJECT_ID" IS
              NOT NULL)
       filter("OWNER"='DINGJUN123')
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
       3763  consistent gets
          0  physical reads
          0  redo size
     268134  bytes sent via SQL*Net to client
       2275  bytes received via SQL*Net from client
        171  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       2540  rows processed

[email protected]> SELECT COUNT(*) FROM t WHERE object_id >= 100;
  COUNT(*)
----------
   1032649


 SELECT COUNT(*) FROM t WHERE object_id >= 100;
返回1032649行,但是WHERE owner='DINGJUN123' AND object_id>=100 只返回2540行,要filter掉百萬行,輪詢索引,造成了極大的浪費。


3.如果都是比較,都是<,>之類的表示式
  這種情況,前導列,根據謂詞,選擇條件能夠定位最接近處理結果的基數,並能夠減少索引後filter的工作
,因為必然有一列是要走access之後的filter,最好是filter能夠過濾較少資料,不要做過多過濾。
  
例如:
[email protected]> SELECT * FROM t
  2  WHERE owner>='DINGJUN123'
  3  AND object_id>=107872;

37 rows selected.
Elapsed: 00:00:00.00
Execution Plan
----------------------------------------------------------
Plan hash value: 3787301248
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |   205 | 19680 |    43   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |   205 | 19680 |    43   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IDX2_T |   205 |       |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID">=107872 AND "OWNER">='DINGJUN123' AND "OBJECT_ID"
              IS NOT NULL)
       filter("OWNER">='DINGJUN123')

Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         14  consistent gets
          0  physical reads
          0  redo size
       6468  bytes sent via SQL*Net to client
        438  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         37  rows processed

[email protected]> SELECT COUNT(*) FROM t WHERE object_id>=107872;
  COUNT(*)
----------
        37
1 row selected.


  關閉index SKIP SCAN,因為owner種類很少,oracle選擇skip SCAN
 alter session set "_optimizer_skip_scan_enabled" = false;
 
[email protected]> SELECT/*+index(t idx1_t)*/ * FROM t
  2  WHERE owner>='DINGJUN123'
  3  AND object_id>=107872;
37 rows selected.
Elapsed: 00:00:00.23
Execution Plan
----------------------------------------------------------
Plan hash value: 2071967826
--------------------------------------------------------------------------------------
| Id  | Operation                   | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |        |   205 | 19680 |  3740   (1)| 00:00:45 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T      |   205 | 19680 |  3740   (1)| 00:00:45 |
|*  2 |   INDEX RANGE SCAN          | IDX1_T |   205 |       |  3700   (1)| 00:00:45 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OWNER">='DINGJUN123' AND "OBJECT_ID">=107872 AND "OWNER" IS
              NOT NULL)
       filter("OBJECT_ID">=107872)
Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
 3688  consistent gets
          0  physical reads
          0  redo size
       6468  bytes sent via SQL*Net to client
        438  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         37  rows processed


因為owner>='DINGJUN123'返回大量行,但是事實結果很少,只有幾十行,過濾object_id>=107872,需要做大量工作,邏輯讀和COST增大千倍+,效能低下。


後記:
     當然如何選擇前導列的順序很複雜,得全盤考慮對應的謂詞,SELECT的列等要素,還要考慮ORDER BY ,GROUP BY等列,比如3列組合索引,如何考慮順序。
     後續會補充更多的組合索引如何建立的要點。

相關推薦

Oracle index組合索引如何選擇前導考慮

選擇組合索引的前導列,必須根據具體的業務(SQL)寫法和列的資料分佈不同而不同,很多書或網上都說,前導列要選擇高選擇性的,但是,脫離具體的業務,這些是沒有意義的,本文就舉一些常見的例子來分析下如何正確選擇前導列,以拋磚引玉,實際應用中,有更多複雜的情況需要具體分析。1.都是

Ensemble methods組合方法&集成方法

zed 加權 最好 rdquo ear 整合 表示 ensemble st算法   機器學習的算法中,討論的最多的是某種特定的算法,比如Decision Tree,KNN等,在實際工作以及kaggle競賽中,Ensemble methods(組合方法)的效果往往是最好的,

TCP/IPIP路由選擇

否則 spa dsm mil adding 搜索 應該 區分 class IP層在內存中有一個路由表,當有數據要發送時,它要對該表進行一次搜索以確認轉發地址。收到的數據到達IP層時。IP層會檢查數據報的目的地址是否為本機IP或廣播IP: 假設是,就依據IP首部

C++ STL容器的選擇

但是 函數 pair list 成員 cto 允許 數據 結構 c++提供了各具特長的容器,那麽我們該如何選擇最佳的容器? 缺省狀態下應該選擇vector,因為vector內部結構最簡單,並允許隨機存取,所以數據的存取十分方便,數據的處理也快。 如果經常要在頭部和尾部安插

Oracle入門數據庫的二級映像

可能 存儲 mil 管理 定義 邏輯 數據 數據庫系統 存儲方式 數據庫系統 的模式、內模式、外模式之間有很大的差別,為了實現用戶和數據之間的透明化,DBMS提供了兩層映像:外模式、模式映像和模式/內模式u映像。有了這兩層映像,用戶就能邏輯地、抽象地處理數據,而不必關旭數據

oracle入門數據模型

平臺 數據庫管理 層次 要素 數據庫管理系統 left 世界 pan 概念 數據模式也是一這種模型,它是數據庫中用於提供信息表示的操作手段的形式架構,是數據庫中用來對現實世界驚喜抽象的工具。數據模型按不同的應用層次分為3種類型,分別為概念數據模型、邏輯數據模型、物理數據模型

oracle入門數據完整性約束

and 對數 關系模型 標識 引用 關系型 條件 一個 定義 數據的完整性約束是對數據描述的某種約束條件,關系型數據模型中可以有三類完整性約束:實體完整性、參照完整性和用戶定義的完整性。 實體完整性Entity Integrity 一個基本關系通

分治法線性時間選擇(轉)

算法思路 ont 位置 劃分 得到 子數組 als lena part 轉自:http://blog.csdn.net/liufeng_king/article/details/8480430 線性時間選擇問題:給定線性序集中n個元素和一個整數k,1≤k≤n,要求找出這n

oracle 優化之組合索引

組合索引適用場景: 1.適用在單獨查詢返回記錄很多,組合查詢後忽然返回記錄很少的情況: 比如where 學歷=碩士以上 返回不少的記錄 比如where 職業=收銀員 同樣返回不少的記錄 於是無論哪個條件查詢做索引,都不合適。 可是,如果學歷為碩士以上,同時職業又是收銀員的,返回的就少之又少了。 於是聯合

機房重構組合查詢--儲存過程

定義 儲存過程(Stored Procedure)是在大型資料庫系統中,一組為了完成特定功能的SQL 語句集,儲存在資料庫中,經過第一次編譯後再次呼叫不需要再次編譯,使用者通過指定儲存過程的名字並給出引數(如果該儲存過程帶有引數)來執行它。儲存過程是資料庫中的一個重要物件。 建立

oracle效能11g生成ASH報告

oracle 11g生成ASH報告 ASH以V$SESSION為基礎,每秒取樣一次,記錄活動會話等待的事件。不活動的會話不會取樣,取樣工作由新引入的後臺程序MMNL來完成。ASH buffers 的最小值為1MB,最大值不超過30MB.記憶體中記錄資料。期望值是記錄一小時的內容。 資料庫

oracle恢復通過閃回(flahback)找回刪除的表

oracle誤刪的表通過閃回(flash back)找回 場景: 資料庫版本:11.2.0.4單例項;系統版本:Oracle Linux6.4 子公司聯絡人反饋,某公司人員對庫進行了誤操作,導致有六張基礎表被誤刪除,看能否找回相關表,並把相關使用者名稱atist和表名資訊傳送過來。 解決

ORACLE效能ORACLE伺服器的CPU和負載均衡過高

ORACLE伺服器的CPU和負載均衡過高 場景: 資料庫版本:11.2.0.4 RAC;系統版本:Oracle Linux 6.4 巡檢發現DDDRAC庫CPU/負載均衡過高,load(15m)值達到了40以上,CPU值達到90%以上。 解決: 發現CPU和過載過高後,檢視磁碟I

oracle效能檢視阻塞表的相關資訊

oracle檢視阻塞表的相關資訊 select t2.username,t2.sid || ',' ||t2.serial# as "sid_terial#",t2.SQL_ID,do.owner, do.OBJECT_NAME,t2.osuser,t2.machine,t2.logon_t

Java學習JFileChooser(檔案選擇器)的使用

一、概述。 javax.swing.JFileChooser()(檔案選擇器)提供了一種檔案選擇機制,一般用於開啟檔案,儲存檔案。 二、常用方法。 構造器: 1. public JFileChooser() : 構造一個JFileChooser物件,預設開啟的資料

15.6.2Task使用 組合異步操作

中一 取字符 接受 具體化 別人 sync 承諾 complete 累加   對於C# 5異步特性,我最喜歡的一點是它可以自然而然地組合在一起。這表現為兩種不同的 方式。最明顯的是,異步方法返回任務,並通常會調用其他返回任務的方法。這些方法可以是直 接的異步操作(如鏈的最底

面試題資料庫索引及B樹、B+樹詳解

最近準備找一個實習,所以接下來,會通過其他人分享的面經陸續的總結面試中經常遇到的題 今天是關於資料庫索引,以及具體的實現(B樹及B+樹) 本文參考自兩篇部落格(個人認為是最好的相關部落格了) 資料庫索引部分:http://blog.csdn.net/weilianglian

設計模式組合模式

模式定義 組合模式允許你將物件組合成樹形結構來表現“整體/部分”層次結構。組合能讓客戶以一致的方式處理個別對象以及物件組合。 下圖是該模式的類圖: 可以實現下圖的需求: 一個生動的例子 元件抽象類: public abstract class MenuCompon

Oracle 11gR2靜默安裝 db_install.rsp檔案詳解

#################################################################### ## Copyright(c) Oracle Corporation1998,2008. All rights reserved. ## ##

oracle筆記關於windows中安裝Oracle的SQLPlus

windows中安裝Oracle的SQLPlus 說明一下: 由於自己在伺服器上安裝了一個oracle資料庫,但是資料庫中只有資料庫,也就是沒有任何客戶端等介面,我在本地安裝的pl/sql等一些連線工具,但是突然想在伺服器做一些工作,因為這樣伺服器上跑查詢資料命令自己就可