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


0、序言

本文我們來談談項目中常用的 20 條 MySQL 優化方法,效率至少提高 3倍!

具體如下:

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

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

2、必須被索引

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

3、聯合索引

對於聯合索引來說,如果存在范圍查詢,比如between、>、<等條件時,會造成后面的索引字段失效。

對於聯合索引來說,要遵守最左前綴法則:舉列來說索引含有字段 id、name、school,可以直接用 id 字段,也可以 id、name 這樣的順序,但是 name; school 都無法使用這個索引。所以在創建聯合索引的時候一定要注意索引字段順序,常用的查詢字段放在最前面。

4、強制索引

必要時可以使用 force index 來強制查詢走某個索引: 有的時候MySQL優化器采取它認為合適的索引來檢索 SQL 語句,但是可能它所采用的索引並不是我們想要的。這時就可以采用 forceindex 來強制優化器使用我們制定的索引。

5、日期時間類型

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

盡管 TIMESTAMEP 存儲空間只需要 datetime 的一半,然而由於類型 TIMESTAMP 存在性能問題,建議你還是盡可能使用類型 DATETIME。(TIMESTAMP 日期存儲的上限為 2038-01-19 03:14:07,業務用 TIMESTAMP 存在風險;)

6、禁止使用 SELECT *

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

7、避免出現某些字段

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

8、where 子句

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

避免在where子句中對字段進行表達式操作:因為對字段就行了算術運算,這會造成引擎放棄使用索引。

9、like

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

在日常中你會發現全模糊匹配的查詢,由於 MySQL 的索引是 B+ 樹結構,所以當查詢條件為全模糊時,例如 %AB%%AB,索引無法使用,這時需要通過添加其他選擇度高的列或者條件作為一種補充,從而加快查詢速度。僅AB%形式的可以避免通配符引起索引屏蔽。

10、用 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 了;再或者使用連接來替換。

11、禁止使⽤負向查詢

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

12、范圍查詢

在對字符串類型的索引進行大於運算時,會導致全表掃描。所以應改為區間between區間范圍運算。

13、order by/group by

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

14、禁止使用 order by rand()

order by rand() 會為表增加幾個偽列,然后用 rand() 函數為每一行數據計算 rand() 值,最后基於該行排序,這通常都會生成磁盤上的臨時表,因此效率非常低。建議先使用 rand() 函數獲得隨機的主鍵值,然后通過主鍵獲取數據。

15、盡量用union all代替union

union 和 union all 的差異主要是前者需要將結果集合並后再進行唯一性過濾操作,這就會涉及到排序,增加大量的CPU運算,加大資源消耗及延遲。當然,union all 的前提條件是兩個結果集沒有重復數據。

16、減少與數據庫交互

盡量采用批量 SQL 語句,減少與數據庫交互次數。

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

17、復雜查詢還是簡單查詢?

不要用一個SQL解決所有事情,可以分步驟做,省時、易理解、優化。且 MySQL 也十分擅長處理短而簡單的 SQL,總體耗時會更短,而且也不會產生臃腫的 SQL,讓人難以理解和優化。

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

18、刪除全表數據

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

19、字符集問題

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實戰》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM