sql調優基本步驟

對SQL調優基本步驟:
a) 捕獲SQL語句
b) 產生SQL語句的執行計劃;
c) 驗證統計信息(SQL語句涉及到的表格是否做過分析),表格信息(結果集的記錄數,索引),字段上面數據分佈特點
d) 通過手工收集到的信息,形成自己理想的執行計劃。
e) 如果做過分析,則重新分析相關表格或者做柱狀圖分析。
f) 如果沒有做過分析,則通過嘗試不同的Hint,從而獲得合適的執行計劃。
g) 當我們正常無法調優到位時,可以打開10053事件打開優化器的跟蹤,看看Oracle如何選擇的.
alter session set events='10053 trace name context forever,level 2';

常見的問題:
1. Statement not written for indexes 25%
2. Indexes are missing or inappropriate 16%
3. Use of single-column index merge 15%
4. Misuse of nested loop, sort merge, or hash join 12%
5. Misuse of IN, EXISTS, NOT IN, NOT EXISTS, or table joins 8%

-----------

捕獲sql:

查看statistics_level參數是否開啓:
show parameter statistics_level;

開啓awr
alter system set statistics_level=all;

查看awr配置:
select * from dba_hist_wr_control;

修改awr配置:
execute dbms_workload_repository.modify_snapshot_settings(interval=>30,retention=>14400);
以分鐘爲單位

查看快照:
select * from dba_hist_snapshot;

刪除快照
execute DBMS_WORKLOAD_REPOSITORY.DROP_SNAPSHOT_RANGE(low_snap_id => 1, high_snap_id => 10); 

手動生成快照:
select dbms_workload_repository.create_snapshot() from dual;
返回值是snap_id

******生成報告:
執行腳本$ORACLE_HOME/rdbms/admin,如:
sql>@?/rdbms/admin/awwrrpt.sql        --?代表ORACLE_HOME

awrrpt.sql:根據一段snapid產生一個報告
awrrpti.sql

awrsqrpt.sql:從一段snapid中產生某個sql語句的執行報告
awrsqrpi.sql

awrddrpt.sql:比較兩個時間段間的差異細節
awrddrpi.sql

----------

sql執行計劃:

產生執行計劃:
sqlplus:
SET AUTOT[RACE] {OFF | ON | TRACE[ONLY]} [EXP[LAIN]] [STAT[ISTICS]]
pL/SQL developer:

查看執行計劃目的:
哪個爲驅動表
判斷是否利用了索引
驅動表是否合適,如果不合適,對SQL語句進行更改,使優化器可以選擇正確的驅動表。

計劃層次關係:
PARENT1
**FIRST CHILD
****FIRST GRANDCHILD
**SECOND CHILD 

表訪問方式
Full Table Scan (FTS) 全表掃描
index unique scan   --索引唯一掃描
index range scan   --索引局部掃描 (e.g. > < <> >= <= between)
index full scan   --索引全局掃描(帶order by)
index fast full scan   --索引快速全局掃描,不帶order by情況下常發生
index skip scan   --索引跳躍掃描,where條件列是非索引的前導列情況下常發生
Rowid 物理ID掃描

運算符:
1.sort    --排序,很消耗資源
(1)order by clauses (2)group by (3)sort merge join –-這三個會產生排序運算
2.filter    --過濾,如not in、min函數等容易產生
3.view    --視圖,大都由內聯視圖產生(可能深入到視圖基表)
4.partition view     --分區視圖

------------

查看是否收集了統計信息:

select NUM_ROWS,BLOCKS,LAST_ANALYZED from dba_tables where OWNER='&OWNER' and TABLE_NAME='&TABLE_NAME';

select NUM_ROWS,BLOCKS,LAST_ANALYZED from dba_indexes where INDEX_NAME='&INDEX_NAME' and OWNER='&OWNER';

查看是否自動收集統計信息
show parameter STATISTICS_LEVEL = {ALL | TYPICAL | BASIC}
select owner,job_name,last_start_date,last_run_duration,next_run_date from dba_scheduler_jobs where job_name='GATHER_STATS_JOB';
10開始建庫後默認創建gather_stats_job的定時任務,每晚22:00-06:00和週末全天開啓(數據庫時間)

-----------

統計信息存放:
tables
user_tab_statistics
user_tables
user_tab_partitions
user_tab_subpartitions

columns
user_tab_col_statistics
user_tab_histograms
user_part_col_statistics
user_part_histograms
user_subpart_col_statistics
user_subpart_histograms

indexes         
user_ind_statistics
user_indexes
user_ind_statistics

-----------

手動收集統計信息:
gather_table_stats
gather_index_stats
gather_schema_stats
gather_database_stats(需要很長時間)
gather_stale_stats
例如:
1) exec dbms_stats.gather_table_stats('ZQB','T');
2) execute dbms_stats.gather_schema_stats(ownname=>'ZQB',estimate_percent=>dbms_stats.auto_sample_size,method_opt=>'for all columns size auto')

--------------

自動收集統計信息:
exec dbms_scheduler.enable('SYS.GATHER_STATS_JOB');
exec dbms_scheduler.disable('SYS.GATHER_STATS_JOB');

----------

查看字段上的數據分佈特點:
select cdrtype,count(*) from cardsusage200508 group by cdrtype;
CDRT   COUNT(*)
---- ----------
102         637
106     1973757
107     2390097
112       46016
113          20
對於數據分佈不均勻的列要收集直方圖信息

----------

查看直方圖:
select ENDPOINT_NUMBER,ENDPOINT_VALUE from dba_histograms where TABLE_NAME='&TABLE_NAME' and OWNER='&OWNER' and COLUMN_NAME='&COLUMN_NAME';

-----------

收集直方圖:
execute dbms_stats.gather_table_stats ('&OWNER','&TABLE_NAME',method_opt=>'for columns size auto');
column size auto讓oracle自動決定哪些列需要收集直方圖信息,以及桶的數據

execute dbms_stats.gather_table_stats ('&OWNER','&TABLE_NAME',method_opt=>'for columns size 10 id');
在id列上建10個桶

-----------

查看錶的記錄數:
全表:count
where過濾以後的記錄數:select count(0) from table_name where ...

----------

分析多表連接方式:
NESTED LOOP
1) 適用於被連接的數據子集較小的情況
2) 沒有索引一般不會是 nested loops,適合走hash join,因爲不需要索引。
3) 如果驅動表(外循環表)返回記錄太多,就不適合nested loops了。
4) 用USE_NL(table_name1 table_name2)提示來強制使用nested loop。

HASH JOIN
1) 原理:
Hash join是優化器做大數據集連接時的常用方式。優化器掃描小表(或數據源),利用連接鍵(也就是根據連接字段計算hash 值)在內存中建立hash表,然後掃描大表,每讀到一條記錄就來探測hash表一次,找出與hash表匹配的行。
2) 使用hash join時,HASH_AREA_SIZE初始化參數必須足夠的大,如果是9i,Oracle建議使用SQL工作區自動管理,設置WORKAREA_SIZE_POLICY 爲AUTO,然後調整PGA_AGGREGATE_TARGET即可。
3)適用:
兩個巨大的表之間的連接
在一個巨大的表和一個小表之間的連接
4) 用USE_HASH(table_name1 table_name2)提示來強制使用hash join

SORT MERGE JOIN
1) 原理
Sort merge join的操作通常分三步:對連接的每個表做table access full;對table access full的結果進行排序;進行merge join對排序結果進行合併。sort merge join性能開銷幾乎都在前兩步
2) 適用:
通常情況下hash join的效果都比sort merge join要好,然而如果行源已經被排過序,在執行sort merge join時不需要再排序了,這時sort merge join的性能會優於hash join
3) 用USE_MERGE(table_name1 table_name2)提示強制使用sort merge join

---------

嘗試不同的hint,獲取最優執行計劃:
Hint全集

/*+ parallel(table_name,8) */並行執行,並行度<=邏輯cpu數

/*+ORDERED*/
根據表出現在FROM中的順序,ORDERED使ORACLE依此順序對其連接.例如:
SELECT /*+ORDERED*/ A.COL1,B.COL2,C.COL3 FROM TABLE1 A,TABLE2 B,TABLE3 C
WHERE A.COL1=B.COL1 AND B.COL1=C.COL1;

/*+USE_NL(TABLE)*/
將指定表與嵌套的連接的行源進行連接,並把指定表作爲內部表.例如:
SELECT /*+ORDERED USE_NL(BSEMPMS)*/ DPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

/*+USE_MERGE(TABLE)*/
將指定的表與其他行源通過合併排序連接方式連接起來.例如:
SELECT /*+USE_MERGE(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

/*+USE_HASH(TABLE)*/
將指定的表與其他行源通過哈希連接方式連接起來.例如:
SELECT /*+USE_HASH(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE
BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

/*+CACHE(TABLE)*/
當進行全表掃描時,CACHE提示能夠將表的檢索塊放置在緩衝區緩存中最近最少列表LRU的最近使用端例如:
SELECT /*+FULL(BSEMPMS) CAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;

/*+NOCACHE(TABLE)*/
當進行全表掃描時,CACHE提示能夠將表的檢索塊放置在緩衝區緩存中最近最少列表LRU的最近使用端,例如:
SELECT /*+FULL(BSEMPMS) NOCAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;

/*+ FULL(TABLE)*/
表明對錶選擇全局掃描的方法.例如:
SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO='CCBZZP';

/*+ INDEX(TABLE INDEX_NAME)*/
表明對錶選擇索引的掃描方法.例如:
SELECT /*+INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX='M';

/*+INDEX_ASC(TABLE INDEX_NAME)*/
表明對錶選擇索引升序的掃描方法.例如:
SELECT /*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='CCBZZP';

/*+INDEX_COMBINE*/
爲指定表選擇位圖訪問路經,如果INDEX_COMBINE中沒有提供作爲參數的索引,將選擇出位圖索引的布爾組合方式.例如:
SELECT /*+INDEX_COMBINE(BSEMPMS SAL_BMI HIREDATE_BMI)*/ * FROM BSEMPMS
WHERE SAL<5000000 AND HIREDATE

/*+INDEX_JOIN(TABLE INDEX_NAME)*/
提示明確命令優化器使用索引作爲訪問路徑.例如:
SELECT /*+INDEX_JOIN(BSEMPMS SAL_HMI HIREDATE_BMI)*/ SAL,HIREDATE
FROM BSEMPMS WHERE SAL<60000;

/*+INDEX_DESC(TABLE INDEX_NAME)*/
表明對錶選擇索引降序的掃描方法.例如:
SELECT /*+INDEX_DESC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='CCBZZP';

/*+INDEX_FFS(TABLE INDEX_NAME)*/
對指定的表執行快速全索引掃描,而不是全表掃描的辦法.例如:
SELECT /*+INDEX_FFS(BSEMPMS IN_EMPNAM)*/ * FROM BSEMPMS WHERE DPT_NO='TEC305';

/*+ADD_EQUAL TABLE INDEX_NAM1,INDEX_NAM2,...*/
提示明確進行執行規劃的選擇,將幾個單列索引的掃描合起來.例如:
SELECT /*+INDEX_FFS(BSEMPMS IN_DPTNO,IN_EMPNO,IN_SEX)*/ * FROM BSEMPMS WHERE 
EMP_NO='CCBZZP' AND DPT_NO='TDC306';

/*+USE_CONCAT*/
對查詢中的WHERE後面的OR條件進行轉換爲UNION ALL的組合查詢.例如:
SELECT /*+USE_CONCAT*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';

/*+ALL_ROWS*/
表明對語句塊選擇基於開銷的優化方法,並獲得最佳吞吐量,使資源消耗最小化.例如:
SELECT /*+ALL+_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP';

/*+FIRST_ROWS*/
表明對語句塊選擇基於開銷的優化方法,並獲得最佳響應時間,使資源消耗最小化.例如:
SELECT /*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP';

/*+CHOOSE*/
表明如果數據字典中有訪問表的統計信息,將基於開銷的優化方法,並獲得最佳的吞吐量;表明如果數據字典中沒有訪問表的統計信息,將基於規則開銷的優化方法;例如:
SELECT /*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP';

/*+ RULE*/
表明對語句塊選擇基於規則的優化方法.例如:
SELECT /*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='CCBZZP'; 

/*+ROWID(TABLE)*/
提示明確表明對指定表根據ROWID進行訪問.例如:
SELECT /*+ROWID(BSEMPMS)*/ * FROM BSEMPMS WHERE ROWID>='AAAAAAAAAAAAAA'
AND EMP_NO='CCBZZP';

/*+CLUSTER(TABLE)*/ 
提示明確表明對指定表選擇簇掃描的訪問方法,它只對簇對象有效.例如:
SELECT /*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS
WHERE DPT_NO='TEC304' AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;

/*+NO_EXPAND*/
對於WHERE後面的OR 或者IN-LIST的查詢語句,NO_EXPAND將阻止其基於優化器對其進行擴展.例如:
SELECT /*+NO_EXPAND*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';

/*+NOWRITE*/
禁止對查詢塊的查詢重寫操作.

/*+REWRITE*/
可以將視圖作爲參數.

/*+MERGE(TABLE)*/
能夠對視圖的各個查詢進行相應的合併.例如:
SELECT /*+MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO
,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) Va WHERE A.DPT_NO=V.DPT_NO
AND A.SAL>V.AVG_SAL;

/*+NO_MERGE(TABLE)*/
對於有可合併的視圖不再合併.例如:
SELECT /*+NO_MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO
AND A.SAL>V.AVG_SAL;

/*+DRIVING_SITE(TABLE)*/
強制與ORACLE所選擇的位置不同的表進行查詢執行.例如:
SELECT /*+DRIVING_SITE(DEPT)*/ * FROM BSEMPMS,DEPT@BSDPTMS WHERE 
BSEMPMS.DPT_NO=DEPT.DPT_NO;

/*+LEADING(TABLE)*/
將指定的表作爲連接次序中的首表.

/*+APPEND*/
直接插入到表的最後,可以提高速度.
insert /*+append*/ into test1 select * from test4 ;

/*+NOAPPEND*/
通過在插入語句生存期內停止並行模式來啓動常規插入.
insert /*+noappend*/ into test1 select * from test4;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章