SQL是的全稱是Structured Query Language(結構化查詢語言)。SQL是一個在80年代中期被使用的工業標準數據庫查詢語言。不要把SQL語言
與商業化產品如Microsoft SQL server或開源產品MySQL相混淆。全部的使用SQL縮略詞的這些都是SQL標準的一部分。
1、SQL tuning以前的調整
下面這個粗略的方法可以節省數千小時乏味的SQL tuning,由於一旦調整它將影響數以百計的SQL查詢。記住,你必須優先調整它,不然後
續的優化器參數改變或統計信息可能不會有助於你的SQL調整。數據庫
記住,你應當老是優先考慮系統級別的SQL tuning,不然在SQL tuning以後再進行調整可能會使得你先前調整的SQL功虧一簣。
一、優化系統內核
首先應當考慮調整磁盤和網絡I/O子系統(象RAID,DASD帶寬,網絡等)去最小化I/O時間,網絡包的大小以及調度頻率。
二、調整優化器統計信息
應當按期收集和存儲優化器的統計信息以便優化器根據數據的分佈生成最佳的執行計劃。此外,直方圖有助於優化表的鏈接以及爲有傾斜的
where 子句謂詞信息作出正確的訪問決定。
三、調整優化器參數
下列優化器參數應當被調整
optimizer_mode, optimizer_index_caching, optimizer_index_cost_adj
四、優化實例
下列實例/會話級別參數將影響SQL性能
db_block_size,db_cache_size, and OS parameters (db_file_multiblock_read_count, cpu_count, &c),
五、使用索引或物化視圖調整SQL訪問負載
Oracle 10g以後可使用SQL Access advisor來爲SQL生成索引或物化視圖的建議。應當老是使用索引來優化SQL,特別是基於函數的索引。
Oracle 11g的改進:
Oracle 11g中新增的SQL Performance Analyzer (SPA)是一個從總體上加快SQL調整的新特性。
經過SPA,一旦建立一個負載(稱爲SQL tuning set,或者STS),Oracle將根據不一樣環境狀況,使用複雜的預測模塊重複的執行工做負載(使
用迴歸測試方法),來獲得當前負載的最佳SQL執行計劃。使用SPA,咱們能夠預測一個SQL負載基於系統變化形成的影響,以及預測象參數
調整,系統schema調整,硬件調整,操做調整,Oracle升級以後當前SQL語句的響應時間。更多詳細的細節請參考:Oracle 11g New Feature
當運行環境,Oracle實例以及對象被調整以後,更多地關注則是數據庫中的性能影響最大的單個單個的SQL語句。下面將針對單個SQL調整給
出一些常規建議以提升 Oracle 性能。
2、Oracle SQL tuning的目標
Oracle SQL tuning是一個複雜的課題。Oracle Tuning: The Definitive Reference 這整本書描述了關於SQL tuning的細節。儘管如此,
爲了提升系統系能,Oracle DBA應當聽從下面一些總的指導原則。
一、SQL tuning 目標
是以最小的數據庫訪問次數提取更多地數據行來生成最佳的執行計劃(儘量最小化物理讀(PIO)與邏輯讀(LIO)。
指導原則
移除沒必要要的大型全表掃描
大型表的全表掃描將產生龐大的系統I/O且使得整個數據庫性能降低。優化專家首先會評估當前SQL查詢所返回的行數。最多見的辦
法是爲走全表掃描的大表增長索引。B樹索引,位圖索引,以及基於函數的索引等可以避免全表掃描。有時候,對一些沒必要要的全表掃
描經過添加提示的方法來避免全表掃描。
緩存小表全表掃描
有時候全表掃描是最快的訪問方式,管理員應當確保專用的數據緩衝區(keep buffer cache,nk buffer cache)對這些表可用。在
Oracle 8 之後小表能夠被強制緩存到 keep 池。
使用最佳索引
Oracle 訪問對象有時候會有一個以上的索引選擇。所以應當檢查當前查詢對象上的每個索引以確保Oracle使用了最佳索引。
物化聚合運算以靜態化表統計
Oracle 10g的特性之一SQL Access advisor 會給出索引建議以及物化視圖的建議。物化視圖能夠預鏈接表和預摘要表數據。(譯者
按,即Oracle能夠根據特定的更新方式來提早更新物化視圖中的數據,而在查詢時僅僅查詢物化視圖便可獲得最終所需的統計數據
結果。物化視圖其實是一張實體表)
以上這些歸納了SQL tuning的目標。然而看是簡單,調整起來並不容易,由於這須要對Oracle SQL內部有一個完全的瞭解。接下來讓咱們從
總體上來認識 Oracle SQL 優化。
二、Oracle SQL 優化器
Oracle DBA首先要查看的是當前數據庫缺省的優化器模式。Oracle初始化參數提供不少基於成本優化的優化器模式以及以前廢棄的基於規則
的優化器模式(或hint)供選擇。基於成本的優化器主要依賴於表對象使用analyze命令收集的統計信息。Oracle根據表上的統計信息得以決定
併爲當前的SQL生成最高效的執行計劃。須要注意的是在一些場合基於成本優化器可能會作出不正確的決定。基於成本的優化器在不斷的改進,
可是依然有不少場合使用基於規則的優化器可以使得查詢更高效。
在Oracle 10g以前,Oracle 缺省的優化器模式是CHOOSE模式。在該模式下,若是表對象上缺少統計信息則此時Oracle使用基於規則的優化
器;若是統計信息存在則使用基於成本的優化器。使用CHOOSE模式存在的隱患便是對一些複雜得查詢有些對象上有統計信息,而另外一些對象
缺少統計信息。
在Oracle 10g開始,缺省的優化器模式是 ALL_ROWS,這有助於全表掃描優於索引掃描。ALL_ROWS優化器模式被設計成最小化計算資源且有
助於全表掃描。索引掃描(first_rows_n)增長了額外的I/O開銷。可是他們能更快地返回數據。
緩存
所以,大多數OLTP系統選擇first_rows,first_rows_100 或者 first_rows_10以使得Oracle使用索引掃描來減小讀塊數量。
網絡
注意:從Oracle 9i R2開始,Oracle 性能調整指導指出了first_rows 優化器模式已經被廢棄,且使用first_rows_n代替
當僅有一些表包含CBO統計信息,而另外一些缺少統計信息時,Oracle使用基於成本的優化模式來預估其餘表在運行時的統計信息(即動態採樣
),這在很大程度上影響單個查詢性能降低。
總之,Oracle 數據庫管理員應當老是將嘗試改變優化器模式做爲SQL tuning的第一步。Oracle SQL tuning的首要原則是避免可怕的全表掃
描。一個特性之一是一個非高效的SQL語句爲提升查詢性能使用全部的索引此仍然爲一個失敗的SQL語句。
固然,有些時候使用全表掃描是合適的,尤爲是在作聚合操做象sum,avg等操做,由於爲了得到結果,表上的絕大部分數據行必須被讀入到
緩存。SQL tuning 高手應當合理的評估每個全表掃描並要覈實使用索引可否提升性能。
在大多數Oracle 系統,SQL語句檢索的僅僅是表上數據一個子集。Oracle 優化器會檢查使用索引是否會致使更多的I/O。然而,若是構建了
一個低效的查詢,基於成本的優化器難以選擇最佳的數據訪問路徑,轉而傾向於使用全表掃描。故Oracle數據庫管理員應當老是審查那些走
全表掃描的SQL語句。
更多有關全表掃描的問題,以及選擇正確的優化模式請 :"Oracle Tuning: The Definitive Reference"
3、SQL 調整戰略步驟
不少人問SQL tuning從哪裏着手。首先應當是從Library cache去根據他們的活動情況捕獲SQL語句。
一、尋找影響較大的SQL語句
咱們能夠根據SQL語句執行次數的多少進行排序來得到執行次數較多的SQL語句。在v$sqlarea視圖中executions 列以及表stats$sql_summary
或 dba_hist_sql_summary 可以去定位當前最頻繁使用的SQL語句。注:也能夠按照下列方式列出SQL語句。
Rows processed
處理的行數越多,則相應會有很高的I/O,也有可能耗用大量的臨時表空間
Buffer gets
Buffer gets太高可能代表資源被過分集中化查詢,存在熱塊現象
Disk reads
高的磁盤讀將引發過分的I/O
Memory KB
內存的分配大小能夠鑑別該SQL語句是否在內存中使用了大量的錶鏈接
CPU secs
CPU的開銷代表哪些SQL語句耗用了大量的CPU資源
Sorts
排序越多,則SQL性能越差,並且會佔用大量的臨時表空間
Executions
執行次數代表了當前SQL語句的頻繁度,應當被首先考慮調整,由於這些語句影響了數據庫的總體性能
二、決定SQL的執行計劃
每個SQL語句均可以根據SQL_ID來得到其執行計劃。有大量的第三方工具來得到SQL語句的執行計劃。而得到執行最經常使用的方式是使用Oracle
自帶的explain plan程序。經過使用該程序,Oracle DBA可以在不執行SQL 語句的情形下解析並顯示該SQL語句的執行計劃。
查看SQL執行計劃的輸出,必須首先建立一個plan table. Oracle提供一個utlxplan.sql腳原本建立該表。執行該腳本而且爲該表建立一個
公共同義詞。
sqlplus > @utlxplan
Table created.
sqlplus > create public synonym plan_table for sys.plan_table;
Synonym created.
大多數關係數據庫使用解釋程序將SQL語句做爲輸入,而後運行SQL優化器,輸出訪問的路徑信息到一個plan_table。以便咱們可以查看及調
整其訪問方式。下面的是一個複雜的SQL查詢。
EXPLAIN PLAN SET STATEMENT_ID = 'test1' FOR
SET STATEMENT_ID = 'RUN1'
INTO plan_table
FOR
SELECT 'T'||plansnet.terr_code, 'P'||detplan.pac1
|| detplan.pac2 || detplan.pac3, 'P1', sum(plansnet.ytd_d_ly_tm),
sum(plansnet.ytd_d_ty_tm),
sum(plansnet.jan_d_ly),
sum(plansnet.jan_d_ty),
FROM plansnet, detplan
WHERE
plansnet.mgc = detplan.mktgpm
AND
detplan.pac1 in ('N33','192','195','201','BAI',
'P51','Q27','180','181','183','184','186','188',
'198','204','207','209','211')
GROUP BY 'T'||plansnet.terr_code, 'P'||detplan.pac1 || detplan.pac2 || detplan.pac3;
這個語法使用管道輸入到SQL優化器,解析SQL,存儲執行計劃信息到表plan_table,且RUN1做爲鑑別當前SQL語句的標識符。注意,該查詢
並無執行,它僅僅是建立了一個內部訪問信息且輸出到plan_table。plan 表包含下列字段。
operation
代表當前語句完成的操做,一般包括table access, table merge, sort, or index operation
options
補充說明operation,像full table, range table, join
object_name
查詢組件的名字
Process ID
查詢組件的ID號
Parent_ID
查詢組建的父ID,注意,有些查詢會有一個相同的父ID
如今plan_table已經被填充,可使用下面的查詢來查看當前SQL語句的執行計劃。
plan.sql - displays contents of the explain plan table
SET PAGES 9999;
SELECT lpad(' ',2*(level-1))||operation operation,
options,
object_name,
position
FROM plan_table
START WITH id=0
AND
statement_id = 'RUN1'
CONNECT BY prior id = parent_id
AND
statement_id = 'RUN1';
下面給出了當前語句執行計劃信息以及各個部分的執行順序。
SQL> @list_explain_plan
OPERATION
-------------------------------------------------------------------------------------
OPTIONS OBJECT_NAME POSITION
------------------------------ -------------------------------------------------------
SELECT STATEMENT
SORT
GROUP BY 1
CONCATENATION 1
NESTED LOOPS 1
TABLE ACCESS FULL PLANSNET 1
TABLE ACCESS BY ROWID DETPLAN 2
INDEX RANGE SCAN DETPLAN_INDEX5 1
NESTED LOOPS
從上面的執行計劃中得知當前的SQL語句存在表掃描現象。去調整該SQL語句,咱們應當尋找表where 子句中爲planset上的列。在這裏咱們
看到了在where子句存在一個且屬於表planset上的列mgc被用做鏈接條件。這說明一個基於表planset.mgs列上的索引是必要的。
plan table並不能展示整個SQL語句的細節,但對於得到數據訪問路徑是很是有用的。SQL優化器知道每個表的行數(基數)以及一些索引字
段的情況。但並不瞭解數據的分佈象如一個組件期待返回的行數。
三、調整SQL語句
對於那些存在可優化的子執行計劃,SQL應當按照下面的方式進行調整。
經過添加提示來修改SQL的執行計劃
使用全局臨時表來重寫SQL
使用PL/SQL來重寫SQL。對於一些特定查詢該方法可以有20倍左右的提高。將這些SQL封裝到包含存儲過程的包中去完成查詢。
使用提示來調整SQL
大多數SQL tuning工具中使用較多的莫過於使用提示。一個提示添加的SQL語句後使得SQL查詢的按指定路徑訪問。
Troubleshooting tip!
爲便於測試,咱們可以隨時使用alter session命令來修改一個優化參數的值來觀察調整先後的結果比較。使用新的 opt_param 提示能得到
一樣的效果。
select /*+ opt_param('optimizer_mode','first_rows_10') */ col1, col2 . . .
select /*+ opt_param('optimizer_index_cost_adj',20) */ col1, col2 . .
Oracle 發佈了大量的SQL提示,並且提示隨着Oracle版本的不一樣不斷的加強和複雜化。
注意:提示一般用於調試SQL,最佳的辦法是調整優化器的統計信息使的CBO模式自動獲取最佳執行路徑,等同於使用提示的功能。
咱們來看看提升性能最經常使用的提示
Mode hints: first_rows_10, first_rows_100
Oracle leading and ordered hints Also see how to tune table join order with histograms
Dynamic sampling: dynamic_sampling
Oracle SQL undocumented tuning hints - Guru's only
The cardinality hint
錶鏈接順序
當錶鏈接的順序可優化時,咱們可使用 ORDERED提示來強制表按照from子句中出現的前後順序來進行鏈接
first_rows_n提示
Oracle 有兩個基於成本優化的提示,一個是first_rows_n,一個是all_rows。first_rows模式將盡量在一查詢到數據時就返回個客
戶端。而 all_rows 模式則爲優化資源而設計,須要等到全部結果計算執行完畢才返回數據給客戶端。
SELECT /*+ first_rows */
四、案例
同一個SQL語句有不一樣的寫法。即簡單的SQL查詢可以以不一樣的方式來產生相同的結果集,但其執行效率和訪問方式則千差萬別。
下面的例子中的SQL語句使用了3種不一樣的寫法來返回相同的結果
A standard join: -->標準鏈接
SELECT *
FROM STUDENT, REGISTRATION
WHERE
STUDENT.student_id = REGISTRATION.student_id
AND
REGISTRATION.grade = 'A';
A nested query: -->嵌套查詢
SELECT *
FROM STUDENT
WHERE
student_id =
(SELECT student_id
FROM REGISTRATION
WHERE
grade = 'A'
);
A correlated subquery: -->相關子查詢
SELECT *
FROM STUDENT
WHERE
0 <
(SELECT count(*)
FROM REGISTRATION
WHERE
grade = 'A'
AND
student_id = STUDENT.student_id
);
咱們應該根據基本的SQL原則來優化當前的SQL語句。
五、書寫高效SQL語句的技巧
下面給出一些編寫高效SQL語句的總的指導原則,而不論Oracle優化器選擇何種優化模式。這些看是簡單的方式可是按照他們
去作將收到事半功倍的效果(已經在實踐中被證明)。
a.使用臨時表重寫複雜的子查詢
Oracle 使用全局臨時表以及WITH操做符去解決那些複雜的SQL子查詢。尤爲是那些where子句中的子查詢,SELECT 字句標量子查詢,
FROM 子句的內聯視圖。使用臨時表實現SQL tuning(以及使用WITH的物化視圖)可以使得性能得以驚人的提高。
b.使用MINUS 代替EXIST子查詢
使用MINUS操做代替NOT IN 或NOT EXISTS將產生更高效的執行計劃(譯者按:此須要測試)。
c.使用SQL分析函數
Oracle 分析函數可以一次提取數據來作多維聚合運算(象ROLLUP,CUBE)以提升性能。
d.重寫NOT EXISTS和查詢做爲外部鏈接NOT EXISTS 子查詢
在一些案例中的NOT 查詢(where 中一個列被定義爲NULL值),可以將其改寫這個非相關子查詢到IS NULL 的外部連接。以下例:
select book_key from book
where
book_key NOT IN (select book_key from sales);
下面咱們在where子句中使用了外部鏈接來替代原來的not exits,獲得一個更高效的執行計劃。
select b.book_key from book b, sales s
where
b.book_key = s.book_key(+)
and
s.book_key IS NULL;
e.索引NULL值列
若是你的SQL語句頻繁使用到NULL值,應當考慮基於NULL值建立索引。爲使該查詢最優化,能夠建立一個使用基於NULL值索引函數。
(譯者按,如 create index i_tb_col on tab(nvl(col,null)); create index i_tb_col on tab(col,0);)
f.避免基於索引的運算
不要基於索引列作任何運算,除非你建立了一個相應的索引函數。或者重設設計列以使得where子句列上的謂詞不須要轉換。
-->下面都是低效的SQL寫法
where salary*5 > :myvalue
where substr(ssn,7,4) = "1234"
where to_char(mydate,mon) = "january"
g.避免使用NOT IN 和HAVING
在合適的時候使用not exists子查詢更高效。
h.避免使用LIKE謂詞
在合適地時候,若是可以使用 = 運算應儘量避免LIKE操做。
i.避免數據類型轉換
若是一個where 子句列是數字型,則不要使用引號。而對一個字符索引列,老是使用引號。下面是數據類型混用的情形。
where cust_nbr = "123"
where substr(ssn,7,4) = 1234
j.使用decode與case
使用decode 與case 函數可以最小化查詢表的次數。
k.不要懼怕全表掃描
並非全部的OLTP系統在使用索引時是最優化的。若是你的查詢返回了表中的絕大部分數據,則全表掃描性能優於索引掃描。這取決於
一些因素包括你的配置(db_file_multiblock_read_count, db_block_size),並行查詢,以及表塊和索引塊在buffer cache中的數量。
l.使用別名
在參照列的地方老是使用表別名。
--> Author : Robinson Cheng
--> Blog : http://blog.csdn.net/robinson_0612
4、結論:
這篇文章從總體上描述SQL tuning的一些步驟,並未涉及SQL tuning的具體細節。更多參考:"Oracle Tuning: The Definitive Reference"
session
原文連接:http://www.dba-oracle.com/art_sql_tune.htmoracle
5、更多參考:
函數