天天看點

殺手SQL- 一條關于 'Not in' SQL 的優化案例

編輯手記:在 dba 所優化的資料庫環境中,絕大多數性能問題其實是由于 sql 編寫不當導緻的。sql 的世界無奇不有,今天我們一起見識一條讓你絕對想吐血的殺手sql。

某保險客戶,etl 耗時數個小時,我們做了sql report發現壓力主要在其中一個sql上。

殺手SQL- 一條關于 'Not in' SQL 的優化案例

單次執行時間:5788(秒)

單次邏輯讀:10億(塊)

單次傳回行數:21萬(行)

我們首先看sql語句,因為比較長,此處隻節選部分的

殺手SQL- 一條關于 'Not in' SQL 的優化案例

檢視其執行計劃:

殺手SQL- 一條關于 'Not in' SQL 的優化案例

我們主要關注一下從7到16行:發現存在兩次全表掃描。中間做了一次filter。

多年的經驗告訴我,兩個全表掃組成的filter ,問題很嚴重, 因為涉及資料逐條處理。 而這個執行計劃裡,被驅動表還是全表掃。

not in/in 操作有時候的确會産生 filter操作,在11g之前的版本,要把not in 語句轉換成反連接配接,not in條件的列必須有not null 屬性,或者語句中帶入了not null的限制,否則隻能采用filter,逐條過濾.

我們舉例說明一下:

sql1:create table t_obj as select object_id,owner,object_name,object_type from dba_objects where owner != 'serol';

sql2:create tablet_table as select owner,table_name from dba_tables where owner!='serol';

檢視t_obj的屬性:

殺手SQL- 一條關于 'Not in' SQL 的優化案例

發現有在三列上都沒有not null的限制。

我們此時僞裝成10g的優化器。

sql> alter session set optimizer_features_enable="10.2.0.5";

執行以下sql:

sql> set autotracetrace exp sql> select * from t_table where table_name not in(select object_name from t_obj);

此時檢視執行計劃,我們發現走的是filter:

殺手SQL- 一條關于 'Not in' SQL 的優化案例

但在11g版本中,優化器可以自動把not in操作從昂貴的filter轉換成null-aware-anti-join。

若加個not null 條件或者欄位屬性設為not null

sql> alter table t_obj modify(object_name not null);

再次執行相同語句:

sql> select * from t_table  where table_name not in(select object_name from t_obj whereobject_name is not null);

再次檢視執行計劃:

殺手SQL- 一條關于 'Not in' SQL 的優化案例

此時我們發現,在執行計劃中,走了hash join anti.

并且,在11g裡面,允許not in列沒有not null 限制也可以轉換anti-join.

sql> alter session set optimizer_features_enable="11.2.0.4"; sql> alter table t_obj modify(object_name null); sq>  select * from t_table  where table_name not in (selectobject_namefrom t_obj);

檢視執行計劃:

殺手SQL- 一條關于 'Not in' SQL 的優化案例

我們看到,此時在沒有非空限制的情況下,也走了hash join anti.

這個特性, 可通過優化器參數控制。

sql>alter session set "_optimizer_null_aware_antijoin"=false;

再次執行以上語句并檢視執行計劃:

sql>  select * from t_table  where table_name
殺手SQL- 一條關于 'Not in' SQL 的優化案例

發現仍然走的是hash join anti.

經過驗證,不是這個參數設定問題

not in 的邏輯,就是結果集之間的互斥,其實有多種改寫的方式,比如:

-- not exists

-- outer join + is null

-- minus

not in與以上三種寫法的差別是:not in 是會排斥空值。

我們嘗試改寫。

殺手SQL- 一條關于 'Not in' SQL 的優化案例

接下來正當你以為會發生奇迹的時候,語句報錯了!

殺手SQL- 一條關于 'Not in' SQL 的優化案例

為什麼會報錯呢? 

如果我們把該語句轉換為not in的方式:

殺手SQL- 一條關于 'Not in' SQL 的優化案例

根據not in的邏輯,此時在fee_code前應該加上'a.',當然這也是沒有問題的,但是,再次看這條語句就會變成:

殺手SQL- 一條關于 'Not in' SQL 的優化案例

由于tmp_app_xxx_prem a 中并沒有fee_code字段, 是以,not in 無法自動改成null aware anti join。

是以,至此答案揭曉,竟然是寫錯了?!我猜中了這開頭,卻沒有猜中這結局。

殺手SQL- 一條關于 'Not in' SQL 的優化案例

但在本案例中,由于sql語句中沒有顯式寫出表明,導緻在前期分析過程中一直沒有發現這個錯誤。

你是不是也很無語,其實我更想問的是,你是不是也經常寫出殺手sql呢,但沒關系,你有病我有藥啊。(無辜臉,不要打我)

我們都知道,在 dba 所優化的資料庫環境中,絕大多數性能問題其實是由于 sql 編寫不當導緻的。

sql稽核

sql稽核将 sql 品質稽核和優化這項任務,從 db 端提取到研發端,通過擅長 sql 的開發 dba 和開發團隊一起修正系統的 sql,找出問題、修複問題,提升系統的健壯性和穩定性,進而保證整個系統的運維建設品質。

對于未上線系統,通過前期的sql稽核管控,将80%的sql問題消滅在萌生階段,對于線上運作系統,發現和解決潛在的性能問題,可做到提前預防,防患于未然。

sql稽核,讓dba由系統的急救醫生轉身成為系統的保健醫生

1、dba參與應用代碼開發測試過程:給開發人員提供專業的資料庫開發及優化建議 2、優化前置:在應用代碼上線前根據業務需求設計高效的sql、索引 3、控制變更風險:預先評估應用開發中表結構變更、sql變更對運作中應用的影響,确定合适的變更視窗,變更方案。

sql稽核産品工具 - z3 

雲和恩墨基于oracle資料庫的sql稽核産品工具 - z3 ,通過内置的算法引擎,可定制的抓取資料庫中執行的sql及其詳細資料,通過過濾分析,進行量化的積分趨勢展現,并将sql問題高亮顯示,指導程式員的優化分析,并可以通過内置的工作流由dba進行優化,變更管理同時被内置。