【Oracle性能優化】!=、NOT NULL、+ 優化分析

接下來小編會作不少不少小實驗,有些前提要說明一下,oracle 11g優化器模式Optermizer Mode默認爲all_rows,也就是基於cost和統計信息的模式,咱們就選它作實驗。由於不一樣的優化器模式,一樣的sql語句輸出的執行計劃是不同的,咱們會穿插一些其餘模式的實驗。sql

1、優化技巧及演示

  • 一、!=是不會走索引的,建議使用><進行代替,性能會更好?

網絡上有些文章建議說!=運算符是不走索引的,建議使用><進行代替,性能會更好。小編想了想,真的是這樣的嗎?下面咱們作實驗,先執行下面的前置腳本: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 <也是不走索引的,而是走全表掃描,咱們換幾種優化器模式:函數

從上面三張圖看,當優化器模式分別爲CHOOSERULE纔會走索引,而FIRST ROWS一樣也是不走索引的,所以,咱們說在這種狀況下,選擇優化器模式分別爲CHOOSERULE的性能會相比FIRST_ROWALL 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 nullis 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));

咱們發現該sql實際耗時差很少是6分多鐘,接下來咱們創建一個函數索引,並使用 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工具上的執行計劃:

2、總結

從上文能夠看出,優化器選擇不一樣,執行計劃性能也大有差異,有時候某條sql須要顯示告訴oracle相應的優化器模式怎麼辦呢?好比咱們能夠在sql層面上使用/*+ RULE */顯示指定優化器走RULE模式,如SELECT /*+ RULE */ * FROM TEST_TABLE T

調優時,咱們要選擇最佳的優化器模式進行調優,根據具體業務場景,數據選擇性高低,是否要走索引等等因素,視狀況而定選擇最優的方法。

最後的最後,我只是個開發,還要我學DBA乾的活,還要學運維的docker,我太難了,哈哈,老鐵,關注走一波。

相關文章
相關標籤/搜索