1. 程式人生 > >帶你認識Oracle索引型別(精)

帶你認識Oracle索引型別(精)

Oracle索引和MySQL索引是一個概念,都是為了提高資料庫查詢效率,例如字典的目錄,就是一種索引。不同的索引有不同的查詢效率,比如字典的目錄有以拼音首字母的,有偏旁部首的。當我們對所有索引型別有了瞭解之後,就可以針對性的寫出高效的SQL語句、建立最合適的索引。
那Oracle索引都有哪些型別呢?
首先我們來看一下索引的資料結構:B樹

在介紹之前,首先建立一張表

create table test(
	id number,
	a number,
	b number,
	c number
);
alter table test add constraint pk_test primary key (id) using index;

插入資料

begin
for i in 1 .. 100000 loop
	insert into test
	values
	(i,mod(i,2),mod(i,20000),mod(i,20000));
	end loop;
	commit;
end;

表建好了,有一個主鍵id,和三個值a、b、c。
下面開始我們的表演

INDEX UNIQUE SCAN索引唯一掃描

看下面這個SQL語句

select id from test where id = 5000;

我們知道id是主鍵,具有唯一性,而上述語句的SQL是一個等值條件(id=400,而<、>、>=等都屬於非等值條件),所以該語句執行的索引為INDEX UNIQUE SCAN,這個速度是最快的。
條件:主鍵等值查詢

INDEX RANGE SCAN索引範圍掃描

看下面這個SQL

select id from test where id<5000;

這個屬於主鍵上的非等值查詢條件,走的就是索引範圍掃描,當然還包括非主鍵索引情況。
下面我們再針對a、b、c建立兩個索引:

create index idx_test_id_ab on test(a,b);
create index idx_test_id_c on test(c);

第一個索引是一個a和b的複合索引,第二個索引建立在c上。
我們看下面這個SQL

select * from test where c=200;

該語句也會走索引範圍掃描。
你是否有疑問,我們如何知道語句是走哪種索引的?這可以從Oracle的執行計劃看到,檢視Oracle的執行計劃只需要將你要觀察的索引加入到其監控中。

exec dbms.stats.gather_table_stats('table_space','table_name');
exec dbms.stats.gather_index_stats('table_space','index_name');

條件:主鍵非等值查詢、非主鍵查詢。

INDEX FULL SCAN索引全掃描

對於表來說,有全表掃描。對於索引來說,也存在全索引掃描,與全表掃描非常類似。索引掃描只在CBO模式下起作用。

什麼是CBO模式?這就不得不說Oracle的兩種優化器:
RBO:Rule-Based Optimization,Oracle 10g版本後被棄用,RBO是基於預先設定好的語法優先順序對語法進行執行計劃的優化,所以開發者必須非常瞭解RBO的規則,這種方式非常呆板,因為其只認規則。
CBO:Cost-Based Optimization,Oracle 8引入,Oracle 10g取代了RBO,根據SQL執行情況的統計資訊來對SQL的執行計劃進行優化,這部分是Oracle公司保密的。問過Oracle公司的講師說也不清楚具體細節。

這種方式有個特點,會自動對資料進行排序。省去了全表掃描後,再進行order by的操作。
因為B樹索引本身就是排序好的,預設是ASC升序,可以在建立索引的時候進行指定。但是Oracle的執行計劃會自動針對升序的降序查詢進行優化,那麼為什麼要存在降序操作?答案是:在複合索引上,可以對(a desc,b asc),滿足一定的業務場景。
我們看下面這個SQL

select * from test order by id;

因為排序的條件只有id,並且id已經建立索引,所以執行計劃會被優化成INDEX FULL SCAN。
條件:表和表進行連線查詢,查詢語句中有order by,group by並且子句所有列都在索引中(聯合索引)
小tip:如何看oracle的執行計劃?sqlplus也可,但當然還有更好的方式,我用的Navicat,執行完sql後,可以通過它的“解釋”功能看執行計劃,走的什麼索引型別,走的哪個索引。

INDEX FAST FULL SCAN快速全表掃描

快速全表掃描是掃描索引中的所有資料塊,與全表掃描比,區別就是其不進行排序,即在這種方式下,返回的資料不是以排序的形式。可以多塊讀、並行讀。所以叫FAST。
看下面的語句

select a,b from test where b<1000;

這個語句有兩個特點,第一:返回值a和b都在索引上,第二:查詢條件也在索引上。這條語句通過B樹索引查詢到rowid後,不需要額外在去原來的表裡查資料了。為什麼呢?回憶一下,符合索引包括根、枝、葉,葉子上儲存的是索引值,包括:rowid、鍵值、鍵值長度、所屬標號。看到沒,如果所取的值都在索引上,就可以直接返回了,如果是

select a,b,c from test where b<1000;

這樣返回值多了一個c,並不在複合索引上,所以還會用查到的rowid,去原表中取c的值,這樣就不會走INDEX FAST FULL SCAN了。

INDEX SKIP SCAN索引跳躍掃描

這個也很簡單,在複合索引中,可能會有如下型別資料:

a b
1 1
1 2
1 3
0 4
0 5
0 6

可以看b的值是不同的,a的取值只有0和1。索引跳躍掃描是掃描意思呢?就是當在這樣一種a,b取值的情況下,對a和b建複合索引,oracle的優化器會將其優化成兩個索引,分別是當a=0,a=1時的索引。
那麼我們在a,b上建聯合索引,彷彿有些問題。理論上不會在這樣的一個沒有多大區分度的a值上建索引的,所以一般看到INDEX SKIP SCAN,其一般開銷都很大。

INDEX COMBINE索引組合掃描

當一個查詢語句中,有兩個查詢,這兩個查詢列對應兩個索引值。這種情況下就會出現索引組合掃描。比如:

select * from test where b<1000 and c>200;

ps,這裡假設我們對b和c分別建立的索引。
走INDEX COMBINE會比單獨走b或c的索引,開銷都要小。
有心人是否看到,這個sql要取的值是所有,並非b或c。那麼如果取的b或c呢?看下面的

INDEX JOIN索引聯立

select b,c from test where b<1000 and c>200;

這種情況就是所查詢的值在索引上,可以直接返回,不在用查到的rowid回原表取資料的情況。
嚴格來說,INDEX COMBINE和INDEX JOIN都不能算是一種獨立的索引,只是對開頭的5種索引的一種優化或補充。


到這裡7種索引型別都介紹完了,留一個疑問

INDEX FAST FULL SCAN一定比INDEX FULL SCAN要快嗎?