接下來小編會作不少不少小實驗,有些前提要說明一下,oracle 11g優化器模式Optermizer Mode
默認爲all_rows
,也就是基於cost和統計信息的模式,咱們就選它作實驗。由於不一樣的優化器模式,一樣的sql語句輸出的執行計劃是不同的,咱們會穿插一些其餘模式的實驗。sql
!=
是不會走索引的,建議使用>
或<
進行代替,性能會更好?網絡上有些文章建議說!=
運算符是不走索引的,建議使用>
或<
進行代替,性能會更好。小編想了想,真的是這樣的嗎?下面咱們作實驗,先執行下面的前置腳本:docker
-- 若是存在,則刪除該表
DROP TABLE TEST_TABLE;
-- 基於DBA_OBJECTS創建一張測試表,這張表是沒有任何主鍵、外鍵、索引的
CREATE TABLE TEST_TABLE AS (SELECT * FROM DBA_OBJECTS);
-- 此時咱們表中有7W多條數據,咱們創建一個惟一索引
CREATE UNIQUE INDEX TEST_TABLE_UNIQUE_INDEX ON TEST_TABLE(OBJECT_ID);
-- 開啓SQL追蹤
SET AUTOTRACE ON;
複製代碼
接着執行下面SQL:bash
SELECT * FROM TEST_TABLE T WHERE T.OBJECT_ID != 10
網絡
!=
是不走索引的,而是走全表掃描,咱們換幾種優化器模式:
咱們發現,不管哪一種優化器模式,都是不走索引的,也就說明!=
不走索引是正確的。那麼,!=
的對立條件是> or <
,這種是否是就會走索引呢?執行下面的腳本:oracle
SELECT * FROM TEST_TABLE T WHERE T.OBJECT_ID > 1 OR T.OBJECT_ID < 1;
運維
咱們發現確實> or <
也是不走索引的,而是走全表掃描,咱們換幾種優化器模式:函數
從上面三張圖看,當優化器模式分別爲CHOOSE
和RULE
纔會走索引,而FIRST ROWS
一樣也是不走索引的,所以,咱們說在這種狀況下,選擇優化器模式分別爲CHOOSE
和RULE
的性能會相比FIRST_ROW
和ALL ROWS
好一些。工具
總結一下:性能
操做 | 優化器模式 | 結果 |
---|---|---|
!= | 任何一種模式 | 都不走索引 |
> xx or < xx | ALL ROWS、FIRST ROWS | 全表掃描 |
> xx or < xx | CHOOSE、RULE | 索引掃描 |
下面小編解釋一下,爲何當優化器模式選擇ALL ROWS
時,下面的腳本會走全表掃描:測試
SELECT * FROM TEST_TABLE T WHERE T.OBJECT_ID > 1;
首先ALL ROWS
是基於代價的優化器模式,咱們表中有7W多條數據,而咱們如今卻想查詢OBJECT_ID
從1到7W多的數據,也就是說至關於查詢99.99%的數據,此時oracle優化器會計算咱們查詢的數據佔總數據的比例,若是超過某個閾值,就會走全表掃描,由於若是此時走索引,那麼oracle須要先掃描99.99%的索引塊,再還要掃描99.99%的段數據塊,那還不如直接掃99.99%的數據塊就行,節省性能。
+
是數學函數,是不會走索引的,真的是這樣的嗎?咱們首先執行下面腳本,對比其執行計劃: SELECT * FROM TEST_TABLE T WHERE T.OBJECT_ID + 10 > 70000;
從上面的圖,咱們能夠看出,四種優化器模式均不走索引,原來講法是正確的,所以,咱們能夠得出下面結論,oracle中,函數是不走索引的,包括+
、-
等數學運算。那麼上面的sql該怎麼改呢?其實很簡單,作一下變換就能夠了,腳本以下: SELECT * FROM TEST_TABLE T WHERE T.OBJECT_ID > 69990;
仍是那句話,走索引不必定就能增長性能,要看你查詢的數據量,若是你查詢的數據量超過表中總數據的必定數值(這個數值是優化器運算的),oracle會默認執行全表掃描,由於此時走索引反而會下降性能。
is null
和is not null
,真的是這樣的嗎?首先咱們執行前置腳本,大體意思就是建立一個表,而後插入10,000,000條數據,並在DATA_OBJECT_ID_INDEX
列上創建一個索引DATA_OBJECT_ID_INDEX
-- 若是存在,則刪除該表
DROP TABLE TEST_TABLE;
-- 基於DBA_OBJECTS創建一張測試表,這張表是沒有任何主鍵、外鍵、索引的
CREATE TABLE TEST_TABLE AS (SELECT * FROM DBA_OBJECTS);
-- 此時咱們表中有7W多條數據,咱們創建一個索引
CREATE INDEX DATA_OBJECT_ID_INDEX ON TEST_TABLE(DATA_OBJECT_ID);
-- 賦值1000W條數據
BEGIN
FOR OBJECT_ID IN 80000..10000000 LOOP
insert into TEST_TABLE (OWNER, OBJECT_NAME, SUBOBJECT_NAME, OBJECT_ID, DATA_OBJECT_ID, OBJECT_TYPE, CREATED, LAST_DDL_TIME, TIMESTAMP, STATUS, TEMPORARY, GENERATED, SECONDARY, NAMESPACE, EDITION_NAME)
values ('SYS', 'C_OBJ#', null, OBJECT_ID, OBJECT_ID, 'CLUSTER', to_date('15-08-2009 00:16:51', 'dd-mm-yyyy hh24:mi:ss'), to_date('15-08-2009 00:16:51', 'dd-mm-yyyy hh24:mi:ss'), '2009-08-15:00:16:51', 'VALID', 'N', 'N', 'N', 5, null);
END LOOP;
END;
commit;
複製代碼
其中DATA_OBJECT_ID
這個列是有不少的null
值的,咱們執行下面的腳本,並查看其執行計劃:
SELECT * FROM TEST_TABLE T WHERE T.DATA_OBJECT_ID IS NULL;
複製代碼
查看V$SQL
查看咱們剛剛執行的SQL_ID
:
SELECT SQL_ID,SQL_TEXT
FROM V$SQL
WHERE SQL_TEXT LIKE '%SELECT * FROM TEST_TABLE T WHERE T.DATA_OBJECT_ID IS NULL%';
複製代碼
最後經過查詢DBMS_XPLAN.DISPLAY_CURSOR
來獲取該SQL的實際執行計劃:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('8v9nk8bdhq30r',0));
nvl()
函數代替
IS NULL
進行查詢,咱們看看其耗時:
CREATE INDEX DATA_OBJECT_ID_FUN_INDEX ON TEST_TABLE(nvl(DATA_OBJECT_ID,0));
SELECT * FROM TEST_TABLE T WHERE NVL(T.DATA_OBJECT_ID,0)=0;
SELECT SQL_ID,SQL_TEXT
FROM V$SQL
WHERE SQL_TEXT LIKE '%SELECT * FROM TEST_TABLE T WHERE NVL(T.DATA_OBJECT_ID,0)=0%';
複製代碼
SQL_ID
,並查看其實際執行計劃:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('0rkf81cym166f',0));
咱們發現加了索引以後,一樣功能的查詢語句,耗時竟然變成6s,難以想象。
最後說明一下,小編之因此經過V$SQL
查找SQL_ID
以後再查看執行計劃,實際上這是有講究的,你使用plsql
工具按F5
或者執行EXPLAIN PLAN FOR XXXXXX
查看到的執行計劃只是Oracle優化器預估出來的,可能會有實際誤差因此說執行計劃有時候是假的,哈哈!不過通常差異不大。下面貼出在plsql
工具上的執行計劃:
從上文能夠看出,優化器選擇不一樣,執行計劃性能也大有差異,有時候某條sql須要顯示告訴oracle相應的優化器模式怎麼辦呢?好比咱們能夠在sql層面上使用/*+ RULE */
顯示指定優化器走RULE
模式,如SELECT /*+ RULE */ * FROM TEST_TABLE T
。
調優時,咱們要選擇最佳的優化器模式進行調優,根據具體業務場景,數據選擇性高低,是否要走索引等等因素,視狀況而定選擇最優的方法。
最後的最後,我只是個開發,還要我學DBA乾的活,還要學運維的docker,我太難了,哈哈,老鐵,關注走一波。