【Oracle index】組合索引如何選擇前導列的幾點考慮
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/IP】IP路由選擇
否則 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.2【Task使用】 組合異步操作
中一 取字符 接受 具體化 別人 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等一些連線工具,但是突然想在伺服器做一些工作,因為這樣伺服器上跑查詢資料命令自己就可