數據庫查詢優化-20條必備sql優化技巧

0、序言

本文咱們來談談項目中經常使用的 20 條 MySQL 優化方法,效率至少提升 3倍!前端

具體以下:面試

一、使⽤ EXPLAIN 分析 SQL 語句是否合理

使⽤ EXPLAIN 判斷 SQL 語句是否合理使用索引,儘可能避免 extra 列出現:Using File Sort、Using Temporary 等。sql

二、必須被索引

重要SQL必須被索引:update、delete 的 where 條件列、order by、group by、distinct 字段、多表 join 字段。數據庫

三、聯合索引

對於聯合索引來講,若是存在範圍查詢,好比between、>、<等條件時,會形成後面的索引字段失效。數組

對於聯合索引來講,要遵照最左前綴法則:舉列來講索引含有字段 id、name、school,能夠直接用 id 字段,也能夠 id、name 這樣的順序,可是 name; school 都沒法使用這個索引。因此在建立聯合索引的時候必定要注意索引字段順序,經常使用的查詢字段放在最前面。網絡

四、強制索引

必要時可使用 force index 來強制查詢走某個索引: 有的時候MySQL優化器採起它認爲合適的索引來檢索 SQL 語句,可是可能它所採用的索引並非咱們想要的。這時就能夠採用 forceindex 來強制優化器使用咱們制定的索引。架構

五、日期時間類型

對於非標準的日期字段,例如字符串的日期字段,進行分區裁剪查詢時會致使沒法識辨,依舊走全表掃描。併發

儘可能使用 TIMESTAMEP 類型,由於其存儲空間只須要 datetime 的一半。函數

六、禁止使用 SELECT *

SELECT 只獲取必要的字段,禁止使用 SELECT *。這樣能減小沒必要要的消耗(CPU、IO、內存、網絡帶寬),增長使用覆蓋索引的可能性;當表結構發生改變時,表結構變動對前端程序基本無影響。高併發

七、避免出現某些字段

SQL 中避免出現 now()rand()sysdate()current_user() 等不肯定結果的函數。在語句級複製場景下,引發主從數據不一致;不肯定值的函數,產生的 SQL 語句沒法使用 QUERY CACHE。

八、where 子句

避免在 where 子句中對字段進行 null 值判斷:對於 null 的判斷會致使引擎放棄使用索引而進行全表掃描。

避免在where子句中對字段進行表達式操做:由於對字段就好了算術運算,這會形成引擎放棄使用索引。

九、like

禁止使用 % 前導查詢,例如:like 「%abc」,⽆法利⽤到索引。

在平常中你會發現全模糊匹配的查詢,因爲 MySQL 的索引是 B+ 樹結構,因此當查詢條件爲全模糊時,例如 %AB%%AB,索引沒法使用,這時須要經過添加其餘選擇度高的列或者條件做爲一種補充,從而加快查詢速度。僅AB%形式的能夠避免通配符引發索引屏蔽。

十、用 IN 代替 OR

OR 兩邊的字段中,若是有一個不是索引字段,而其它條件也不是索引字段,會形成該查詢不走索引的狀況。不少時候都會使用 IN 進行替代,或者使用 union all 或者是 union(必要的時候)的方式來代替「or」也會獲得更好的效果。但 SQL 語句中 IN 包含的值不宜過多,應少於 1000 個。過多會使隨機 IO 增大,影響性能。

使用 IN 是由於 MySQL 對其作了相應的優化,即將 IN 中的常量所有存儲在一個數組裏面,並且這個數組是排好序的。可是若是數值較多,產生的消耗比較大。

再例如:

select id from t where num in(1,2,3)

對於連續的數值,能用 between 就不要用 in 了;再或者使用鏈接來替換。

十一、禁止使⽤負向查詢

禁止使⽤負向查詢,例如:not in、!=、<>、not like。

十二、範圍查詢

在對字符串類型的索引進行大於運算時,會致使全表掃描。因此應改成區間between區間範圍運算。

1三、order by/group by

另外 order by/group by 的 SQL 涉及排序,儘可能在索引中包含排序字段,並讓排序字段的排序順序與索引列中的順序相同,這樣能夠避免排序或減小排序次數。若是排序字段沒有用到索引,就儘可能少排序。

1四、禁止使用 order by rand()

order by rand() 會爲表增長几個僞列,而後用 rand() 函數爲每一行數據計算 rand() 值,最後基於該行排序,這一般都會生成磁盤上的臨時表,所以效率很是低。建議先使用 rand() 函數得到隨機的主鍵值,而後經過主鍵獲取數據。

1五、儘可能用union all代替union

union 和 union all 的差別主要是前者須要將結果集合並後再進行惟一性過濾操做,這就會涉及到排序,增長大量的CPU運算,加大資源消耗及延遲。固然,union all 的前提條件是兩個結果集沒有重複數據。

1六、減小與數據庫交互

儘可能採用批量 SQL 語句,減小與數據庫交互次數。

獲取⼤量數據時,建議分批次獲取數據,每次獲取數據少於 5000 條,結果集應⼩於 1M。

1七、複雜查詢仍是簡單查詢?

不要用一個SQL解決全部事情,能夠分步驟作,省時、易理解、優化。且 MySQL 也十分擅長處理短而簡單的 SQL,整體耗時會更短,並且也不會產生臃腫的 SQL,讓人難以理解和優化。

拆分複雜 SQL 爲多個 小SQL,避免⼤事務。簡單的 SQL 容易使用到 MySQL 的 QUERY CACHE;減小鎖表時間特別是 MyISAM;可使用多核 CPU。

1八、刪除全表數據

delete from table_name;會產生大量 undo 和 redo 日誌,執行時間很長,可採用 TRUNCATE TABLE tablename;

1九、字符集問題

col_utf8mb4 = col_utf8 關聯類型都是 varchar ,但字符集不一樣,沒法使用索引。使用過程當中要特別注意。

20、count 優化

這也是一個被面試中常常會問到的問題,對於下面的四條 SELECT 語句:

select count(*) from table … ;

select count(1) from table … ;

select count(primary key) from table … ;

select count(index key) from table …;

哪一條的執行效率最高呢?這個問題須要具體問題具體分析,不能一律而論。這裏舉 SELECT count(1) 這條 SQL 爲例。

圖片來源於:《拉勾教育專欄:高性能MySQL實戰》

優化前和優化後,執行效率相差2倍。就添加了一個索引。

優化思路 : 是選擇索引 key_len 最短的二級索引效率高,不要使用全表掃描(PK 聚族索引會全表掃描),由於索引 key_len 越短,讀取頁面越少,進而 IO_COST 越小。

小結

  • 大量的更新/刪除操做須要控制頻度,例如:每秒操做2000行如下
  • 使用 prepared statement 和綁定變量,能夠提高性能並避免 SQL 注入
  • 程序應有捕獲 SQL 異常的處理機制,必要時經過 rollback 顯示回滾
  • 儘可能少使用 distinct、order by、group by、union 等 SQL,排序需求能夠放到前端(分頁的就不方便交給前端排序)。
  • 大事務或者長查詢的需求根據業務特色拆分
  • 杜絕程序中在處理事務時夾雜 RPC,會形成資源長時間不釋放。有不少鎖超時、併發數上漲都是因爲事務中有 RPC 形成的。
  • 關注軟件自己的優化同時,也須要關注硬件的性能指標和優化,以及硬件的發展方向。MySQL 屬於 IO 密集型的應用,對存儲硬件的 IO 性能要求比較高,在高併發的場景中,建議使用 PCI-e。

重點總結一下:SQL 的執行過程->查詢優化器的工做原理->SQL 執行計劃的解讀->MySQL 慢查詢日誌和分析->SQL 經常使用的優化手段->SQL 編寫規範->深刻實際業務對數據庫訪問進行優化。

參考:

  • 《數據庫高效優化:架構、規範與SQL技巧》
  • 《拉勾教育專欄:高性能MySQL實戰》
相關文章
相關標籤/搜索