SQL 優化之該走索引卻不走索引的分析(二)

2021-09-06 02:07:13 字數 3851 閱讀 8162

sql優化器簡介

基於規則的優化器

。總是使用索引

。總是從驅動表開始(from子句最右邊的表)

。只有在不可避免的情況下,才使用全表掃瞄

。任何索引都可以

基於成本的優化器

。需要表、索引的統計資料

analyze table customer compute statistics;

analyze table customer estimate statistics sample 5000 rows;

。表中設定並行度、表分割槽

優化器模式

rule模式

。總忽略cbo和統計資訊而基於規則

choose模式

。oracle根據情況選擇rule or first_rows or all_rows

first_rows 模式

。基於成本,以最快的速度返回記錄,會造成總體查詢速度的下降或消耗更多的資源,傾向索引掃瞄,適合oltp系統

all_rows模式

。基於成本,確保總體查詢時間最短,傾向並行全表掃瞄

例如:

select last_name from customer order by last_name;用first_rows時,迅速返回記錄,但i/o量大,用all_rows時,返回記錄慢,但使用資源少。

本文的目的:

1、說一說oracle的optimizer及其相關的一些知識。

2、回答一下為什麼有時乙個表的某個字段明明有索引,當觀察一些sql的執行計畫時,發現確不走索引的問題。

3、如果你對 first_rows、 all_rows這兩種模式有疑惑時也可以看一下這篇文章。

開始吧:

oracle在執行乙個sql之前,首先要分析一下語句的執行計畫,然後再按執行計畫去執行。分析語句的執行計畫的工作是由優化器(optimizer)來完成的。不同的情況,一條sql可能有多種執行計畫,但在某一時點,一定只有一種執行計畫是最優的,花費時間是最少的。相信你一定會用pl/sql developer、toad等工具去看乙個語句的執行計畫,不過你可能對rule、choose、first rows、all rows這幾項有疑問,因為我當初也是這樣的,那時我也疑惑為什麼選了以上的不同的項,執行計畫就變了?

1、優化器的優化方式

oracle的優化器共有兩種的優化方式,即基於規則的優化方式(rule-based optimization,簡稱為rbo)和基於代價的優化方式(cost-based optimization,簡稱為cbo)。

a、rbo方式:優化器在分析sql語句時,所遵循的是oracle內部預定的一些規則。比如我們常見的,當乙個where子句中的一列有索引時去走索引。

b、cbo方式:依詞義可知,它是看語句的代價(cost)了,這裡的代價主要指cpu和記憶體。優化器在判斷是否用這種方式時,主要參照的是表及索引的統計資訊。統計資訊給出表的大小 、有少行、每行的長度等資訊。這些統計資訊起初在庫內是沒有的,是你在做analyze後才出現的,很多的時侯過期統計資訊會令優化器做出乙個錯誤的執行計畫,因些我們應及時更新這些資訊。在oracle8及以後的版本,oracle列推薦用cbo的方式。

我們要明了,不一定走索引就是優的 ,比如乙個表只有兩行資料,一次io就可以完成全表的檢索,而此時走索引時則需要兩次io,這時對這個表做全表掃瞄(full table scan)是最好的。

2、優化器的優化模式(optermizer mode)

優化模式包括rule,choose,first rows,all rows這四種方式,也就是我們以上所提及的。如下我解釋一下:

rule:不用多說,即走基於規則的方式。

choolse:這是我們應觀注的,預設的情況下oracle用的便是這種方式。指的是當乙個表或或索引有統計資訊,則走cbo的方式,如果表或索引沒統計資訊,表又不是特別的小,而且相應的列有索引時,那麼就走索引,走rbo的方式。

first rows:它與choose方式是類似的,所不同的是當乙個表有統計資訊時,它將是以最快的方式返回查詢的最先的幾行,從總體上減少了響應時間。

all rows:也就是我們所說的cost的方式,當乙個表有統計資訊時,它將以最快的方式返回表的所有的行,從總體上提高查詢的吞吐量。沒有統計資訊則走基於規則的方式。

3、如何設定選用哪種優化模式

a、instance級別

我們可以通過在init.ora檔案中設定optimizer_mode=rule、optimizer_mode=choose、optimizer_mode=first_rows、optimizer_mode=all_rows去選用3所提的四種方式,如果你沒設定optimizer_mode引數則預設用的是choose這種方式。

b、sessions級別

通過sql> alter session set optimizer_mode=;來設定。

c、語句級別

這些需要用到hint,比如:

sql> select /*+ rule */ a.userid,

2 b.name,

3 b.depart_name

4 from tf_f_yhda a,

5 tf_f_depart b

6 where a.userid=b.userid;

4、為什麼有時乙個表的某個字段明明有索引,當觀察一些語的執行計畫確不走索引呢?如何解決呢 ?

a、不走索引大體有以下幾個原因

♀你在instance級別所用的是all_rows的方式

♀你的表的統計資訊(最可能的原因)

♀你的表很小,上文提到過的,oracle的優化器認為不值得走索引。

b、解決方法

♀可以修改init.ora中的optimizer_mode這個引數,把它改為rule或choose,重起資料庫。也可以使用4中所提的hint.

補充:不走索引的原因,甚至加上 /*+index(table_name index_name)*/還不走索引,那可能是因為你要走索引的這列是nullable,雖然這列沒有空值。

備註 : 不走索引的其它原因

1

、建立組合索引,但查詢謂詞並未使用組合索引的第一列,此處有乙個index skip scan概念。

2、在包含有null值的table列上建立索引,當時使用select count(*) from table時不會使用索引。

3、在索引列上使用函式時不會使用索引,如果一定要使用索引只能建立函式索引。

4、當被索引的列進行隱式的型別轉換時不會使用索引。如:select * from t where indexed_column = 5,而indexed_column列建立索引但型別是字元型,這時oracle會產生

隱式的型別轉換,轉換後的語句類似於select * from t where to_number(indexed_column) = 5,此時不走索引的情況類似於case3。日期轉換也有類似問題,如:

select * from t where trunc(date_col) = trunc(sysdate)其中date_col為索引列,這樣寫不會走索引,可改寫成select * from t where date_col >= trunc(sysdate)

and date_col < trunc(sysdate+1),此查詢會走索引。

5、並不是所有情況使用索引都會加快查詢速度,full scan table 有時會更快,尤其是當查詢的資料量佔整個表的比重較大時,因為full scan table採用的是多塊讀,

當oracle優化器沒有選擇使用索引時不要立即強制使用,要充分證明使用索引確實查詢更快時再使用強制索引。

6、<>

7、like』%dd』百分號在前

8、not in ,not exist. 

建了索引卻不走索引案例分析

這個部落格和我生產碰到的很像 create tablet order idbigint 20 unsigned not null auto increment,order codechar 12 not null,order amountdecimal 12,2 not null,primary k...

mysql之不走索引和sql優化

一.不走索引的情況 使用 和 不等於符號查詢 使用 not in not exists 查詢 使用like字尾查詢,如 string 在建立索引和沒有索引的字段上使用or,會導致查詢不走索引 使用 符號,可能不走索引,這個要看優化器的判斷 字串型別字段使用數字進行比較,不走索引,如 varchar ...

YII強制走索引

在專案中由於查詢條件異常繁雜,導致sql不會走索引 比如查詢兩個條件,增加 or 這樣就倒是這個不走索引 問題解決啟發聯想 so 直接上 model new yii db query select id,username,first uid,second uid,created at from pr...