使用Oracle數據庫的應用系統,有時出現SQL性能忽然變差,特別是對於OLTP類型系統執行頻繁的核心SQL,若是出現性能問題,一般會影響整個數據庫的性能,進而影響整個系統的正常運行。這是經常遇到的問題,也是一些DBA的挑戰。
sql
SQL的性能變差,一般是在SQL語句從新進行了解析,解析時使用了錯誤的執行計劃出現的。數據庫
下列狀況是SQL會從新解析的緣由:ide
SQL語句沒有使用綁定變量,這樣SQL每次執行都要解析。性能
SQL長時間沒有執行,被刷出SHARED POOL,再次執行時須要從新解析。測試
在SQL引用的對象(表、視圖等)上執行了DDL操做,甚至是結構發生了變化,好比建了一個索引。優化
對SQL引用的對象進行了權限更改。對象
從新分析(收集統計信息)了SQL引用的表和索引,或者表和索引統計信息被刪除。索引
修改了與性能相關的部分參數。文檔
刷新了共享池。it
固然重啓數據庫也會使全部SQL所有從新解析。
SQL從新解析後,跟之前相比,性能忽然變差,一般是下列緣由:
1. 表和索引的優化統計信息被刪除,或者從新收集後統計信息不許確。從新收集統計信息一般是因爲收集策略(方法)不正確引發。好比對分區表使用analyze命令而不是用dbms_stats包、收集統計信息時採樣比例太小等等。Oracle優化器嚴重依賴於統計信息,若是統計信息有問題,則很容易致使SQL不能使用正確的執行計劃。
2. SQL綁定變量窺探(bind peeking),同時綁定變量對應的列上有直方圖;或者綁定變量的值變化範圍過大、分區數據分佈極不均勻:
1) 綁定變量的列上有直方圖:
假如表orders存儲全部的訂單,state列有3種不一樣的值:0表示未處理,1表示處理成功完成,2表示處理失敗。State列上有一個索引,表中絕大部分數據的state列爲1,0和2佔少數。有下面的SQL:
select * from orders where state=:b1
這裏:b1是變量,在大多數狀況下這個值爲0,則應該使用索引,可是若是SQL被從新解析,而第一次執行時應用傳給變量b1值爲1,則不會使用索引,採用全表掃描的方式來訪問表。對於綁定變量的SQL,只在第一次執行時纔會進行綁定變量窺探,並以此肯定執行計劃,該SQL後續執行時所有按這個執行計劃。這樣在後續執行時,b1變量傳入的值爲0的時候,仍然是第一次執行時產生的執行計劃,即便用的是全表掃描,這樣會致使性能不好。
2) 綁定變量的值變化範圍過大:
一樣假如orders表有一列created_date表示一筆訂單的下單時間,orders表裏面存儲了最近1年的數據,有以下的SQL:
select * from orders where created_date >=:b1;
假如大多數狀況下,應用傳入的b1變量值爲最近幾天內的日期值,那麼SQL使用的是created_date列上的索引,而若是b1變量值爲5個月以前的一個值,那麼就會使用全表掃描。與上面描述的直方圖引發的問題同樣,若是SQL第1次執行時傳入的變量值引發的是全表掃描,那麼將該SQL後續執行時都使用了全表掃描,從而影響了性能。
3) 分區數據量不均勻:
對於範圍和列表分區,可能存在各個分區之間數據量極不均勻的狀況下。好比分區表orders按地區area進行了分區,P1分區只有幾千行,而P2分區有200萬行數據。同時假若有一列product_id,其上有一個本地分區索引,有以下的SQL:
select * from orders where area=:b1 and produce_id=:b2;
這條SQL因爲有area條件,所以會使用分區排除。若是第1 次執行時應用傳給b1變量的值正好落在P1分區上,極可能致使SQL採用全表掃描訪問,如前面所描述的,致使SQL後續執行時所有使用了全表掃描。
3. 其餘緣由,好比表作了相似於MOVE操做以後,索引不可用,對索引進行了更改。固然這種狀況是屬於維護不當引發的問題,不在本文討論的範圍。
綜上所述,SQL語句性能忽然變差,主要是由於綁定變量和統計信息的緣由。注意這裏只討論了忽然變差的狀況,而對於因爲數據量和業務量的增長性能逐步變差的狀況不討論。
爲保持SQL性能或者說是執行計劃的穩定性,須要從如下幾個方面着手:
1. 規劃好優化統計信息的收集策略。對於Oracle 10g來講,默認的策略可以知足大部分需求,可是默認的收集策略會過多地收集列上的直方圖。因爲綁定變量與直方圖固有的矛盾,爲保持性能穩定,對使用綁定變量的列,不收集列上的直方圖;對的確須要收集直方圖的列,在SQL中該列上的條件就不要用綁定變量。
統計信息收集策略,能夠考慮對大部分表,使用系統默認的收集策略,而對於有問題的,能夠用DBMS_STATS.LOCK_STATS鎖定表的統計信息,避免系統自動收集該表的統計信息,而後編寫腳原本定製地收集表的統計信息。腳本中相似以下:
EXEC dbms_stats.unlock_table_stats...
EXEC dbms_stats.gather_table_stats...
EXEC dbms_stats.lock_table_stats...
2. 修改SQL語句,使用HINT,使SQL語句按HINT指定的執行計劃進行執行。這須要修改應用,同時須要逐條SQL語句進行,加上測試和發佈,時間較長,成本較高,風險也較大。
3. 修改隱含參數」 _optim_peek_user_binds」爲FALSE,修改這個參數可能會引發性能問題(這裏討論的是穩定性問題)。
4. 使用OUTLINE。對於曾經出現過執行計劃忽然變差的SQL語句,可使用OUTLINE來加固其執行計劃。在10g中DBMS_OUTLN.CREATE_OUTLINE能夠根據已有的執行正常的SQL遊標來建立OUTLINE。若是事先對全部頻繁執行的核心SQL使用OUTLINE加執拗行計劃,將最大可能地避免SQL語句性能忽然變差。
注:DBMS_OUTLN能夠經過$ORACLE_HOME/rdbms/admin/dbmsol.sql腳原本安裝。
5. 使用SQL Profile。SQL Profile是Oracle 10g以後的新功能,此處再也不介紹,請參考相應的文檔。
除此以外,能夠調整一些參數避免潛在的問題,好比將"_btree_bitmap_plans"參數設置爲FALSE(這個參數請參考互聯網上的文章或Oracle文檔)。
而在實際工做中,經過使用定製的統計信息收集策略,以及在部分系統上使用OUTLINE,系統基本上不會出現已有的SQL性能忽然變差的狀況。固然也有維護人員操做不當引發的SQL性能忽然變差,好比建了某個索引而沒有收集統計信息,致使SQL使用了新建的索引,而該索引並不適合於那條SQL;維護人員意外刪除了表個索引的統計信息。