現觀察線上系統運行發現,線上某些業務查詢存在等待時間長問題,后核查發現,部分問題出現在對數據庫操作上Cost大部分時間,后根據網上各位前輩提供的優化技巧解決大部分問題,現寫下本篇文章,一來鞏固加深自己學習的優化技巧,二來方便正在為sql優化迷茫的猿友們提供一下思路和方法,共同進步,一起成長~
1、現狀描述
sql執行時間長、數據查詢慢
2、問題對象
sql執行語句(特別是多表多條件關聯查詢數據)
3、理論知識
1、Oracle優化器
sql優化器:sql數據庫中的優化器又叫查詢優化器(QueryOptimizer)。它是SQL分析和執行的優化工具,它負責生成、制定SQL的執行計划。 sql優化器優化方式 基於規則的優化方式(Rule-BasedOptimization,簡稱為RBO) 它根據指定的規則順序,對指定的表進行執行計划的選擇。它着一套嚴格的使用規則,只要你按照它去寫SQL語句,無論數據表中的 內容怎樣,也不會影響到你的“執行計划”,也就是說RB對數據不“敏感”。要求開發人員了解RBO的各項細則。在sql 10g中完全被CBO取代。 基於代價的優化方式(Cost-Based Optimization,簡稱為CBO)。 CBO是一種比RBO更加合理、可靠的優化器,它是從sql 8中開始引入,在sql10g中完全取代RBO。CBO是計算各種可能“執行 計划”的“代價”,即COST,從中選用COST最低的執行方案,作為實際運行方案。它依賴數據庫對象的統計信息,統計信息的准確與否會影響C BO做出最優的選擇。如果對一次執行SQL時發現涉及對象(表、索引等)沒有被分析、統計過,那么sql會采用一種叫做動態采樣的技術, 動態的收集表和索引上的一些數據信息。
2、sql索引
sql索引是一種供服務器在表中快速查找一個行的數據庫結構。合理使用索引能夠大大提高數據庫的運行效率。
在sql中,索引是一種供服務器在表中快速查找一個行的數據庫結構。在數據庫中建立索引主要有以下作用。
(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
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 消息。 盡量避免大事務操作,提高系統並發能力。