挽救數據庫性能的30條黃金法則

  1. 優化查詢,應儘可能避免全表掃描,應該在用於檢索數據和排序數據的字段上創建索引,如where子句用於搜索,order by子句用於排序,因此在這兩個子句涉及到的字段上須要創建索引。數據庫

  2. 應該儘可能避免在where子句中使用否認的操做符,如不等於(!=或<>)、不然數據庫引擎將放棄使用索引而進行全表掃描。併發

  3. 在儘可能避免在where子句中使用或(or)做爲鏈接條件,不然數據庫引擎將放棄使用索引而進行全表掃描。
    以下面的SQL語句可能會帶來性能問題ide

    select id,name,age from persons 
                where name = 'Bill' or age > 30

    因爲這條SQL語句使用了or,因此數據庫引擎會進行全表掃描,爲了不全表掃描,能夠將這條SQL語句改爲下面的形式。函數

    select id,name,age from persons  where name = 'Bill'
    union all
    select id,name,age from persons where num = 20
  4. 應該儘可能避免在where子句中使用null進行判斷,不然數據庫引擎將放棄使用索引而進行全表掃描。
    先看下面的SQL語句:

select id,name,age from persons where age is null性能

爲了不使用null,能夠設置age字段的默認值爲0,這樣就能夠經過下面的SQL語句達到一樣的結果。
select id,name,age from persons where age = 0優化

  1. 儘可能不用使用like檢索數據,由於也會致使數據庫引擎將放棄使用索引而進行全表掃描。

例如,下面的SQL語句執行的效率會很是低:
select id,name,age from persons where name like '%John%'code

若是真想進行模糊查詢,可使用全文檢索。orm

  1. 在where子句中應儘可能避免在字段中使用表達式(包括函數運算、算數運算等),不然據庫引擎將放棄使用索引而進行全表掃描。
    例如,下面的SQL語句執行的性能比較差
    select id,name,age from persons age / 2 &gt; 12

應該利用表達式變換,改爲下面的形式:
select id,name,age from persons age &gt; 2 * 12排序

或者乾脆改爲下面的形式:
select id,name,age from persons age &gt; 24索引

  1. 應儘可能避免使用in和not in,不然也會致使全表掃描。

如並不推薦下面的寫法:
select id, name,age from persons where age in (22,23,24)

若是數值是連續的,應該使用between,而不要用in,若是數值是不連續的,能夠分紅多個SQL,用union all鏈接查詢結果。

select id,name,age from persons where age between 22 and 24

select id,name,age from persons where age = 22
union all
select id,name,age from persons where age = 26
union all
select id,name,age from persons where age = 30
  1. 應該儘可能避免在where子句中使用參數,不然也將致使全表掃描。這是由於參數須要在SQL運行時才進行替換,而SQL優化(使用索引屬於優化的一部分)是在編譯時進行的。因此數據庫引擎在檢索到參數時,因爲參數的具體指是未知的,因此也就沒法優化了,固然也就沒法使用索引了。

不使用索引的SQL語句:
select id,name,age from persons where name = @name

爲了使用索引,能夠改爲下面強制使用索引的方式:
select id,name,age from persons with(index(name_index)) where name = @name

其中name_index是創建在name字段上的索引名。

  1. 儘可能不要執行一些沒意義的查詢,如條件徹底爲false的查詢:
    select id,name,age into persons1 from persons where age &lt; 0

這樣的代碼會返回一個空結果集,並且會大量消耗系統資源,若是真的想建一個空表,應該直接用create table語句。

  1. 若是使用的索引是符合索引,只有使用該符合索引的第1個字段做爲條件時才能保證數據庫引擎使用該符合索引,不然該符合索引不會被使用。而且應該儘量讓字段順序與索引順序一致。例如,name_index是first_name和last_name字段的符合索引,使用下面的SQL語句會使用該索引。
select id,first_name,last_name
            from persons
                   where first_name = 'Bill'
  1. 若是非要在SQL語句中使用in,那麼使用exists代替in是一個好主意:
select id,num from t 
       where num in (select num from h)

應該用下面的SQL語句代替:

select id,num form t
       where exists(select 0 from h where num = t.num)
  1. 索引並非在任什麼時候候都有效,若是索引列有大量重複的數據,那麼數據庫引擎可能不會去利用索引。例如,sex字段的值只有兩種可能:male和female,可能這兩個值各佔一半,這樣在sex字段上創建索引就沒有任何意義。

  2. 能使用數值型字段就使用數值型字段。由於比較數值型字段的效率要遠比字符型字段的效率高,這是由於比較字符型的值,要一個字母一個字母地比較,而數值型的值,只是比較一個數。因此若是隻包含數值信息的值,應該儘可能使用數值類型的字段。例如,age、salary等。

  3. 應儘可能避免使用固定長度的字段,如char、nchar。使用可變長度的字段是一個很是好的選擇。由於可變長度字段佔用的空間是按需分配的,因此佔用空間比較少。對於查詢來講,毫無疑問,固然是佔用空間小的字段的查詢效率更高了。

  4. 儘可能按需返回字段和記錄,例如:
    select id,name,age from persons where age &gt; 20

儘可能如要使用「」返回全部不須要的字段,也不須要一下就查詢出全部的記錄,以下面的SQL語句在數據量很大時查詢效率是很是低的。
`select
from persons`

  1. 索引有利有弊,增長索引,能夠提升select的執行效率,但付出的代價是在進行insert和update操做時,可能會下降效率。由於進行insert和update操做時一般須要重建索引。因此在一個表中並非索引越多越好。個人建議以下:
    (1)若是一個表大多數時進行的是select操做,那麼索引多一些大多數時候確實能夠提高性能,但這有一個前提,就是不能頻繁進行insert和update操做。
    (2)一個表中的索引數不能太多,最好不要超過6個,不然就好考慮優化一下數據庫了。

  2. 應儘量的避免更新 clustered 索引數據列,由於 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。若應用系統須要頻繁更新 clustered 索引數據列,那麼須要考慮是否應將該索引建爲 clustered 索引。

  3. 應儘可能避免向客戶端返回大理數據,若是數據量過大,應該改變一下需求,或採用分頁返回的方式,如使用MySQL中的limit子句如今返回的數據。

  4. 儘可能避免使用遊標,由於遊標的效率較差,若是遊標操做的數據超過1萬行,那麼就應該採用其餘方案。

  5. 使用基於遊標的方法或臨時表方法以前,應先尋找基於數據集的解決方案來解決問題,基於數據集的方法一般更有效。

  6. 若是使用到了臨時表,在存儲過程的最後務必將全部的臨時表顯式刪除,先用 truncate table清除表中的數據 ,而後 用drop table完全刪除物理表 ,這樣能夠避免系統表的較長時間鎖定。

  7. 避免頻繁建立和刪除臨時表,以減小系統表資源的消耗。

  8. 在新建臨時表時,若是一次性插入的數據量很大,那麼可使用 select into 代替 create table,避免形成大量 log ,以提升執行效率;若是數據量不大,爲了緩和系統表的資源,應先create table,而後使用insert插入數據。

  9. 在全部的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每一個語句後向客戶端發送 DONE_IN_PROC 消息。

  10. 儘可能避免大事務操做,提升系統併發能力。

  11. 應儘可能一次性插入多條數據,例如,使用下面的SQL語句性能會很低:
insert into persons(id,name,age) values('Bill',24)
insert into persons(id,name,age) values('Mike',26)
insert into persons(id,name,age) values('John',20)

爲了提高性能,能夠一次性插入這3條記錄。
insert into persons(id,name,age) values('Bill',24),('Mike',26),('John',20)

  1. 若是不得不使用like進行模糊查詢時,不要在關鍵字前面加%。

反例:

select id,name,age from persons where name like '%abc%'

若是在關鍵字前面加%,那麼查詢是確定要走全表查詢的。

正例:
select id,name,age from persons where name like 'abc%'

  1. 儘可能用union all代替union

union和union all的差別主要是前者須要將兩個(或者多個)結果集合並後再進行惟一性過濾操做,這就會涉及到排序,增長大量的cpu運算,加大資源消耗及延遲。因此當咱們能夠確認不可能出現重複結果集或者不在意重複結果集的時候,儘可能使用union all而不是union。

29.儘可能使用等值鏈接

等值鏈接就是inner join,也稱爲內聯進,而left join和right join是外鏈接。

先看下面的SQL語句

select  a.id,a.name,b.id,b.name from a left join b on a.id = b.id
select  a.id,a.name,b.id,b.name from a right join b on a.id = b.id
select  a.id,a.name,b.id,b.name from a inner join b on a.id = b.id

上面的3條SQL語句,前兩條分別使用了左鏈接和右鏈接,而最後一條使用了內鏈接,通過實際運行,使用內鏈接的SQL語句的執行效率明顯優於左鏈接和右鏈接。因此在能知足需求的前提下,應該儘量使用內鏈接(等值鏈接)。

  1. 儘可能用外鏈接來替換子查詢
    反例
    select id,name from a where exists (select id from b where id&gt;=10 and a.product_id=b.product_id)

在上面的SQL語句中,數據庫引擎會先對外表a執行全表查詢,而後根據product_id逐個執行子查詢,若是外層表(a表)中的數據很是多,查詢性能會很是糟糕。因此應該將SQL語句改爲下面的形式:

select id,name from a inner join b on A.product_id=b.product_id where b.id&gt;=10

相關文章
相關標籤/搜索