互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享
來自:https://blog.csdn.net/bless2015/article/details/84134361
雖然你這列上建了索引,查詢條件也是索引列,但最終執(zhí)行計(jì)劃沒有走它的索引。下面是引起這種問題的幾個(gè)關(guān)鍵點(diǎn)。列與列對比
某個(gè)表中,有兩列(id和c_id)都建了單獨(dú)索引,下面這種查詢條件不會走索引select * from test where id=c_id;
select * from test where id is not null;
NOT條件
我們知道建立索引時(shí),給每一個(gè)索引列建立一個(gè)條目,如果查詢條件為等值或范圍查詢時(shí),索引可以根據(jù)查詢條件去找對應(yīng)的條目。反過來當(dāng)查詢條件為非時(shí),索引定位就困難了,執(zhí)行計(jì)劃此時(shí)可能更傾向于全表掃描,這類的查詢條件有:<>、NOT、in、not existsselect * from test where id<>500;
select * from test where id in (1,2,3,4,5);
select * from test where not in (6,7,8,9,0);
select * from test where not exists (select 1 from test_02 where test_02.id=test.id);
LIKE通配符
當(dāng)使用模糊搜索時(shí),盡量采用后置的通配符,例如:name||’%’,因?yàn)樽咚饕龝r(shí),其會從前去匹配索引列,這時(shí)候是可以找到的,如果采用前匹配,那么查索引就會很麻煩,比如查詢所有姓張的人,就可以去搜索’張%’。相反如果你查詢所有叫‘明’的人,那么只能是%明。這時(shí)候索引如何定位呢?前匹配的情況下,執(zhí)行計(jì)劃會更傾向于選擇全表掃描。后匹配可以走INDEX RANGE SCAN。所以業(yè)務(wù)設(shè)計(jì)的時(shí)候,盡量考慮到模糊搜索的問題,要更多的使用后置通配符。select * from test where name like 張||'%';
條件上包括函數(shù)
查詢條件上盡量不要對索引列使用函數(shù),比如下面這個(gè)SQLselect * from test where upper(name)='SUNYANG';
這樣是不會走索引的,因?yàn)樗饕诮r(shí)會和計(jì)算后可能不同,無法定位到索引。但如果查詢條件不是對索引列進(jìn)行計(jì)算,那么依然可以走索引。比如select * from test where name=upper('sunyang');
--INDEX RANGE SCAN
這樣的函數(shù)還有:to_char、to_date、to_number、trunc等復(fù)合索引前導(dǎo)列區(qū)分大
當(dāng)復(fù)合索引前導(dǎo)列區(qū)分小的時(shí)候,我們有INDEX SKIP SCAN,當(dāng)前導(dǎo)列區(qū)分度大,且查后導(dǎo)列的時(shí)候,前導(dǎo)列的分裂會非常耗資源,執(zhí)行計(jì)劃想,還不如全表掃描來的快,然后就索引失效了。select * from test where owner='sunyang';
數(shù)據(jù)類型的轉(zhuǎn)換
當(dāng)查詢條件存在隱式轉(zhuǎn)換時(shí),索引會失效。比如在數(shù)據(jù)庫里id存的number類型,但是在查詢時(shí),卻用了下面的形式:select * from sunyang where id='123';
Connect By Level
使用connect by level時(shí),不會走索引。謂詞運(yùn)算
我們在上面說,不能對索引列進(jìn)行函數(shù)運(yùn)算,這也包括加減乘除的謂詞運(yùn)算,這也會使索引失效。建立一個(gè)sunyang表,索引為id,看這個(gè)SQL:select * from sunyang where id/2=:type_id;
這里很明顯對索引列id進(jìn)行了’/2’除二運(yùn)算,這時(shí)候就會索引失效,這種情況應(yīng)該改寫為:select * from sunyang where id=:type_id*2;
Vistual Index
先說明一下,虛擬索引的建立是否有用,需要看具體的執(zhí)行計(jì)劃,如果起作用就可以建一個(gè),如果不起作用就算了。普通索引這么建:create index idx_test_id on test(id);
create index idx_test_id on test(id) nosegment;
做了一個(gè)實(shí)驗(yàn),首先創(chuàng)建一個(gè)表:CREATE TABLE test_1116(
id number,
a number
);
CREATE INDEX idx_test_1116_id on test_1116(id);
CREATE INDEX idx_test_1116_a on test_1116(a)nosegment;
begin
for i in 1 .. 100000 loop
insert into test_1116 values (i,i);
end loop;
commit;
end;
接著分別去執(zhí)行下面的SQL看時(shí)間,由于在內(nèi)網(wǎng)機(jī)做實(shí)驗(yàn),圖貼不出來,數(shù)據(jù)保證真實(shí)性。select count(id) from test_1116;
--第一次耗時(shí):0.061秒
--第二次耗時(shí):0.016秒
select count(a) from test_1116;
--第一次耗時(shí):0.031秒
--第二次耗時(shí):0.016秒
因?yàn)樵趫?zhí)行過一次后,oracle對結(jié)果集緩存了,所以第二次執(zhí)行耗時(shí)不走索引,走內(nèi)存就都一樣了。可以看到在這種情況下,虛擬索引比普通索引快了一倍。具體虛擬索引的使用細(xì)節(jié),這里不再展開討論。Invisible Index
Invisible Index是oracle 11g提供的新功能,對優(yōu)化器(還接到前面博客里講到的CBO嗎)不可見,MySQL 也有,MySQL 8.0 中的索引可以隱藏了。我感覺這個(gè)功能更主要的是測試用,假如一個(gè)表上有那么多索引,一個(gè)一個(gè)去看執(zhí)行計(jì)劃調(diào)試就很慢了,這時(shí)候不如建一個(gè)對表和查詢都沒有影響的Invisible Index來進(jìn)行調(diào)試,就顯得很好了。alter index idx_test_id invisible;
alter index idx_test_id visible;
如果想讓CBO看到Invisible Index,需要加入這句:alter session set optimizer_use_invisible_indexes = true;
<END>
掃碼加入技術(shù)交流群,不定時(shí)「送書」
推薦閱讀:
又漲了!2021 年 3 月程序員工資統(tǒng)計(jì)新出爐
能掙錢的,開源 SpringBoot 商城系統(tǒng),功能超全,超漂亮,真TMD香!
最近面試BAT,整理一份面試資料《Java面試BATJ通關(guān)手冊》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點(diǎn)個(gè)「在看」,點(diǎn)擊上方小卡片,進(jìn)入公眾號后回復(fù)「面試題」領(lǐng)取,更多內(nèi)容陸續(xù)奉上。朕已閱 