現觀察線上系統運行發現,線上某些業務查詢存在等待時間長問題,後核查發現,部分問題出現在對資料庫操作上Cost大部分時間,後根據網上各位前輩提供的優化技巧解決大部分問題,現寫下本篇文章,一來鞏固加深自己學習的優化技巧,二來方便正在為sql優化迷茫的猿友們提供一下思路和方法,共同進步,一起成長~ 1、現 ...
現觀察線上系統運行發現,線上某些業務查詢存在等待時間長問題,後核查發現,部分問題出現在對資料庫操作上Cost大部分時間,後根據網上各位前輩提供的優化技巧解決大部分問題,現寫下本篇文章,一來鞏固加深自己學習的優化技巧,二來方便正在為sql優化迷茫的猿友們提供一下思路和方法,共同進步,一起成長~
1、現狀描述
sql執行時間長、數據查詢慢
2、問題對象
sql執行語句(特別是多表多條件關聯查詢數據)
3、理論知識
1、Oracle優化器
Oracle優化器:Oracle資料庫中的優化器又叫查詢優化器(QueryOptimizer)。它是SQL分析和執行的優化工具,它負責生成、制定SQL的執行計劃。
Oracle優化器優化方式
基於規則的優化方式(Rule-BasedOptimization,簡稱為RBO)
它根據指定的規則順序,對指定的表進行執行計劃的選擇。它著一套嚴格的使用規則,只要你按照它去寫SQL語句,無論數據表中的
內容怎樣,也不會影響到你的“執行計劃”,也就是說RB對數據不“敏感”。要求開發人員瞭解RBO的各項細則。在ORACLE
10g中完全被CBO取代。
基於代價的優化方式(Cost-Based Optimization,簡稱為CBO)。
CBO是一種比RBO更加合理、可靠的優化器,它是從ORACLE 8中開始引入,在ORACLE10g中完全取代RBO。CBO是計算各種可能“執行
計劃”的“代價”,即COST,從中選用COST最低的執行方案,作為實際運行方案。它依賴資料庫對象的統計信息,統計信息的準確與否會影響C
BO做出最優的選擇。如果對一次執行SQL時發現涉及對象(表、索引等)沒有被分析、統計過,那麼ORACLE會採用一種叫做動態採樣的技術,
動態的收集表和索引上的一些數據信息。
2、Oracle索引
Oracle索引是一種供伺服器在表中快速查找一個行的資料庫結構。合理使用索引能夠大大提高資料庫的運行效率。
在Oracle中,索引是一種供伺服器在表中快速查找一個行的資料庫結構。在資料庫中建立索引主要有以下作用。
(1)快速存取數據。
(2)既可以改善資料庫性能,又可以保證列值的唯一性。
(3)實現表與表之間的參照完整性
(4)在使用order by、group by子句進行數據檢索時,利用索引可以減少排序和分組的時間。
3、優化方向
a、去掉不必要的大型表的全表掃描
b、去掉不必要的大型表的全表掃描
c、緩存小型表的全表掃描
d、檢驗優化索引的使用
e、檢驗優化的連接技術
f、儘可能減少執行計劃的Cost
4、具體優化方法
1、查詢條件(where後面的子句)優化
避免全表掃描,應考慮在where及order by等列上建立索引,否則將導致進行全表掃描。。
避免在where子句中對欄位進行null值判斷,否則將導致放棄使用索引而進行全表掃描。
避免在where子句中使用!=或<>操作符,否則將導致放棄使用索引而進行全表掃描。
避免用or連接條件,如果有部分欄位存在索引,部分不存在索引,則將導致放棄使用索引而進行全表掃描,建議使用union all代替。
慎用in 和 not in 也要慎用,否則會導致全表掃描。
使用exists替換in問題
子查詢結果集小,用IN
外表小,子查詢表大,用EXISTS
建議實際選取哪個可以對比兩個sql的執行計劃
應儘量避免在 where 子句中對欄位進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
SELECT ID FROM T WHERE NUM / 2 = 100
優化為:
SELECT ID FROM T WHERE NUM = 100 * 2
應儘量避免在where子句中對欄位進行函數函數、算術運算或其他表達式運算操作,否則將導致放棄使用索引而進行全表掃描。如:
-- NAME以ABC開頭的ID
SELECT ID FROM T WHERE SUBSTRING(NAME, 1, 3) = ’ABC’
--2005-11-30’生成的id
SELECT ID FROM T WHERE DATEDIFF(DAY, CREATEDATE, ’2005 - 11 - 30′) = 0
應改為:
SELECT ID FROM T WHERE NAME LIKE 'abc%'
SELECT ID FROM T WHERE CREATEDATE >= '2005-11-30' AND CREATEDATE < '2005-12-1'
2、對結果進行優化
Update 語句,如果只更改1、2個欄位,不要Update全部欄位,否則頻繁調用會引起明顯的性能消耗,同時帶來大量日誌。
對於多張大數據量(這裡幾百條就算大了)的表JOIN,要先分頁再JOIN,否則邏輯讀會很高,性能很差。
select count(*) from table;這樣不帶任何條件的count會引起全表掃描,並且沒有任何業務意義,是一定要杜絕的。
儘量避免向客戶端返回大數據量,若數據量過大,應該考慮是否使用分頁
3、其他優化
索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或
update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到
的列上建的索引是否有必要。
應儘可能的避免更新聚集索引(clustered)數據列,因為聚集索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的
順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新集索引數據列,那麼需要考慮是否應將該索引建為聚集索引
儘量使用數字型欄位,若只含數值信息的欄位儘量不要設計為字元型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因為引擎在處理查詢
和連接時會逐個比較字元串中每一個字元,而對於數字型而言只需要比較一次就夠了。
儘量使用表變數來代替臨時表。如果表變數包含大量數據,請註意索引非常有限(只有主鍵索引)。
避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。臨時表並不是不可使用,適當地使用它們可以使某些常式更有效,例如,當需要重覆引用
大型表或常用表中的某個數據集時。但是,對於一次性事件, 最好使用導出表。
在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量
log,以提高速度;如果數據量不大,為了緩和系統表的資源,應先create table,然後insert。
如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table
,這樣可以避免系統表的較長時間鎖定。
儘量避免使用游標,因為游標的效率較差,如果游標操作的數據超過1萬行,那麼就應該考慮改寫。
使用基於游標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。
與臨時表一樣,游標並不是不可使用。對小型數據集使用 FAST_FORWARD 游標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所
需的數據時。在結果集中包括“合計”的常式通常要比使用游標執行的速度快。如果開發時
間允許,基於游標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。
在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF
。無需在執行存儲過程和觸發器的每個語句後向客戶端發送 DONE_IN_PROC 消息。
儘量避免大事務操作,提高系統併發能力。