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

  1. 優化查詢,應儘量避免全表掃描,應該在用於檢索數據和排序數據的字段上建立索引,如where子句用於搜索,order by子句用於排序,所以在這兩個子句涉及到的字段上需要建立索引。

  2. 應該在where子句中使用否定的操作符,如不等於(!=或<>)、否則數據庫引擎將放棄使用索引而進行全表掃描。

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

    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%'

如果真想進行模糊查詢,可以使用全文檢索。

  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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章