- 數據庫最常用的優化方式有:SQL語句和索引、數據庫表結構、系統配置、硬件。
- 優化效果:SQL語句和索引 < 數據庫表結構 < 系統配置 < 硬件,成本也是遞增的。
優化方法
設計符合範式的數據庫。
- 設計符合範式的數據庫。
- 選擇合適的存儲引擎。
- SQL語句優化;
- 索引優化:高分離字段建立索引。
- SQL表結構、字段優化。
- 數據庫參數優化:IO參數、CPU參數。
- 延遲加載、設置緩存與緩存參數優化。
- 分庫分表:垂直切分與水平切分。
- 分區:將表的數據按照特定的規則放在不同的分區,提高磁盤的IO效率,提高數據庫的性能。
- 主從複製與讀寫分離:三個主要線程與bin-log文件、relay_log文件,主數據庫負責寫操作,從數據庫負責讀操作。
- 負載均衡。
- 數據庫集羣。
- 硬件。
sql語句優化
原則:
- 避免全表掃描,儘量用索引。
1. 寫出統一的SQL語句:
雖然只是大小寫不同,但是查詢分析器認爲是兩句不同的SQL語句,就會進行兩次解析,生成2個執行計劃。所以應該保證相同的查詢語句在任何地方都一致,多一個空格都不行。
select * from dual
select * From dual
2. 應儘量避免在 where 子句中使用!=或<>操作符,否則引擎將放棄使用索引而進行全表掃描。
3. 對查詢進行優化,應儘量避免全表掃描,首先考慮在 where 及 order by 涉及的列上建立索引。
4. 儘量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描。 如:
select id from t where num is null
可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:
select id from t where num=0
5. 避免在where子句中使用or來連接條件,如果一個字段有索引,一個字段沒有索引,將導致引擎放棄使用索引而進行全表掃描。
6. 前導模糊查詢將導致全表掃描:
select id from t where name like '%c%'
使用索引的情況下:
select id from t where name like 'c%'
7. not in 也要慎用
會導致全表掃描。對於連續的數值,能用 between
就不要用 in
了,儘量使用exists
代替in
。
8. 如果在 where 子句中使用參數,也會導致全表掃描。
因爲SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作爲索引選擇的輸入項。如下面語句將進行全表掃描:
select id from t where num=@num
可以改爲強制查詢使用索引:
select id from t with(index(索引名)) where num=@num
7. 應儘量避免在 where 子句中對字段進行表達式與函數或其他表達式運算操作,這將導致引擎放棄使用索引而進行全表掃描。 如:
(1)select id from t where num/2=100
應改爲:select id from t where num=100*2
(2)select id from t where substring(name,1,3)=’abc’ –name以abc開頭的id
應改爲:select id from t where name like 'abc%'
(3)select id from t where datediff(day,createdate,'2005-11-30')=0 –'2005-11-30'生成的id
應改爲:select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
8. Update 語句,如果只更改1、2個字段,不要Update全部字段,否則頻繁調用會引起明顯的性能消耗,同時帶來大量日誌。
9. 在使用索引字段作爲條件時,如果該索引是複合索引,那麼必須使用到該索引中的第一個字段作爲條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應儘可能的讓字段順序與索引順序相一致。
10. 並不是所有索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重複時,SQL查詢可能不會去利用索引。如一表中有字段 sex,male、female幾乎各一半,那麼即使在sex上建了索引也對查詢效率起不了作用。
11. 索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因爲 insert 或 update 時有可能會重建索引。一個表的索引數較好不要超過6個。
12. 應儘可能的避免更新 clustered 索引數據列,因爲 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。
13. 儘量使用數字型字段,若只含數值信息的字段儘量不要設計爲字符型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因爲引擎在處理查詢和連接時會逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了。
14、儘可能的使用 varchar/nvarchar 代替 char/nchar ,因爲首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。
15、任何地方都不要使用 select * from t
,用具體的字段列表代替*
,不要返回用不到的任何字段。
16、對於多張大數據量(這裏幾百條就算大了)的表JOIN,要先分頁再JOIN,否則邏輯讀會很高,性能很差。
17、儘量使用表變量來代替臨時表。
18、考慮使用“臨時表”暫存中間結果。臨時表並不是不可使用,適當地使用它們可以使某些查詢更有效,例如,當需要重複引用大型表或常用表中的某個數據集時。將臨時結果暫存在臨時表,後面的查詢就在tempdb中查詢了,這可以避免程序中多次掃描主表,也大大減少了程序執行中“共享鎖”阻塞“更新鎖”,減少了阻塞,提高了併發性能。但是,對於一次性事件,較好使用導出表。
19、在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,爲了緩和系統表的資源,應先create table,然後insert。
20、如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。
21、避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。
22、儘量避免使用遊標,因爲遊標的效率較差。與臨時表一樣,遊標並不是不可使用。對小型數據集使用 FAST_FORWARD 遊標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數據時。在結果集中包括“合計”的例程通常要比使用遊標執行的速度快。
23、在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。
24、儘量避免向客戶端返回大數據量。
25、儘量避免大事務操作,提高系統併發能力。
26、用where子句替換Having子句:
避免使用having子句,having只會在檢索出所有記錄之後纔會對結果集進行過濾,這個處理需要排序,如果能通過where子句限制記錄的數目,就可以減少這方面的開銷。on、where、having這三個都可以加條件的子句,on是最先執行,where次之,having最後。
27、使用Truncate替代delete:
當需要刪除全表的記錄時使用Truncate替代delete。在通常情況下, 回滾段(rollback segments ) 用來存放可以被恢復的信息. 如果你沒有COMMIT事務,ORACLE會將數據恢復到刪除之前的狀態(準確地說是恢復到執行刪除命令之前的狀況) 而當運用TRUNCATE時, 回滾段不再存放任何可被恢復的信息.當命令運行後,數據不能被恢復.因此很少的資源被調用,執行時間也會很短。
28、使用表的別名:
當在SQL語句中連接多個表時, 請使用表的別名並把別名前綴於每個Column上.這樣一來,就可以減少解析的時間並減少那些由Column歧義引起的語法錯誤。
29、使用union all 替換 union:
當SQL語句需要union兩個查詢結果集合時,這兩個結果集合會以union all的方式被合併,然後再輸出最終結果前進行排序。如果用union all替代料union,這樣排序就不是不要了,效率就會因此得到提高. 需要注意的是,UNION ALL 將重複輸出兩個結果集合中相同記錄。
30、用where替代order by:
ORDER BY 子句只在兩種嚴格的條件下使用索引:
(1)ORDER BY中所有的列必須包含在相同的索引中並保持在索引中的排列順序;
(2)ORDER BY中所有的列必須定義爲非空;
低效: (索引不被使用)
SELECT DEPT_CODE FROM DEPT ORDER BY DEPT_TYPE
高效: (使用索引)
SELECT DEPT_CODE FROM DEPT WHERE DEPT_TYPE > 0
31、避免索引列的類型轉換:
假設EMP_TYPE是一個字符類型的索引列.
SELECT … FROM EMP WHERE EMP_TYPE = 123
這個語句被轉換爲:
SELECT … FROM EMP WHERE EMP_TYPE=‘123’
因爲內部發生的類型轉換, 這個索引將不會被用到!
爲了避免ORACLE對你的SQL進行隱式的類型轉換, 最好把類型轉換用顯式表現出來.
注意當字符和數值比較時, ORACLE會優先轉換數值類型到字符類型。
32、優化Group by:
提高GROUP BY 語句的效率, 可以通過將不需要的記錄在GROUP BY 之前過濾掉.下面兩個查詢返回相同結果但第二個明顯就快了許多。
低效:
SELECT JOB , AVG(SAL)
FROM EMP
GROUP by JOB
HAVING JOB = 'PRESIDENT'
OR JOB = 'MANAGER'
高效:
SELECT JOB , AVG(SAL)
FROM EMP
WHERE JOB = 'PRESIDENT'
OR JOB = 'MANAGER'
GROUP by JOB
33、避免使用耗費資源的操作:
帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY
的SQL語句會啓動SQL引擎執行耗費資源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要執行兩次排序. 通常, 帶有UNION, MINUS , INTERSECT的SQL語句都可以用其他方式重寫. 如果你的數據庫的SORT_AREA_SIZE調配得好, 使用UNION , MINUS, INTERSECT也是可以考慮的, 畢竟它們的可讀性很強。
34、在運行代碼中,儘量使用PreparedStatement來查詢,不要用Statement。