MySQL編程 優化篇(四) SQL優化(ⅳ)常用SQL優化

目錄

 

大批量插入數據

優化order by語句

MySQL中有兩種排序方式

Filesort的優化

優化group by語句

優化嵌套查詢

優化分頁查詢

巧用 Rand() 提取隨機行

數據庫名、表名大小寫問題


大批量插入數據

  1. 因爲InnoDB類型的表是按照主鍵的順序保存的,所以將導入的數據按照主鍵的順序排列,可以有效地提高導入數據的效率。

  2. 在導入前關閉唯一性校驗,導入後開啓唯一性校驗。

  3. 導入前關閉自動提交,導入後開啓自動提交。

優化order by語句

MySQL中有兩種排序方式

  1. 通過有序索引順序掃描直接返回有序數據,這種方式在使用 explain 分析查詢的時候顯示爲Using Index,不需要額外的排序,操作效率較高

  2. 通過對返回數據進行排序,也就是通常說的 Filesort 排序,所有不是通過索引直接返回排序結果的排序都叫Filesort排序。

 

FileSort排序

  • 通過相應的排序算法,將取得的數據在 sort_buffer_size 系統變量設置的 內存排序區 中進行排序,如果內存裝載不下,它就會將磁盤上的數據進行分塊,再對各個數據塊進行排序,然後將各個塊合併成有序的結果集。

  • sort_buffer_size 設置的排序區是每個線程獨佔的,所以同一個時刻,MySQL中存在多個 sort buffer排序區。

 

優化目標:儘量減少額外的排序,通過索引直接返回有序數據

WHERE條件和ORDER BY使用相同的索引,並且ORDER BY的順序和索引順序相同,並且ORDER BY的字段都是升序或者都是降序。否則肯定需要額外的排序操作,這樣就會出現Filesort。

總結,下列SQL可以使用索引:

SELECT * FROM tabname ORDER BY key_part1,key_part2...

SELECT * FROM tabname WHERE key_part1=1 ORDER BY key_part1 DESC,key_ part2 DESC;

SELECT * FROM tabname ORDER BY key_part1 DESC,key_part2 DESC;

但是在以下幾種情況下則不使用索引:

-- order by的字段混合ASC和DESC
SELECT * FROM tabname ORDER BY key_part1 DESC,key_part2 ASC; 

-- 用於查詢行的關鍵字與ORDER BY中所使用的不相同
SELECT * FROM tabname WHERE key2=constant ORDER BY key1;

-- 對不同的關鍵字使用ORDER BY(?)
SELECT * FROM tabname ORDER BY key1, key2;

Filesort的優化

通過創建合適的索引能夠減少Filesort的出現,但是在某些情況下,條件限制不能讓Filesort消失,那就想辦法加快Filesort的操作。對於Filesort,MySQL有兩種排序算法。

  • 兩次掃描算法(Two Passes):首先根據條件取出排序字段和行指針信息,之後在排序區 sort buffer中排序。如果排序區 sort buffer 不夠,則在臨時表 Temporay Table中存儲排序結果。完成排序後根據行指針回表讀取記錄。

    需要兩次訪問數據,第一次獲取排序字段和行指針信息,第二次根據行指針獲取記錄,尤其是第二次讀取操作可能會導致大量隨機IO操作;優點是排序的時候內存開銷少。

  • 一次掃描算法(Single Pass):一次性取出滿足條件的行的所有字段,然後在排序區sort buffer中排序後直接輸出結果集。排序的時候內存開銷比較大,但是排序效率比兩次掃描算法要高。

MySQL通過比較系統變量 max_length_for_sort_data 的大小和 Query 語句取出的字段總大小來判斷使用哪種排序。如果 max_length_for_sort_data 更大,那麼使用第二種優化之後的算法,否則使用第一種算法。

結論

儘量只使用必要的字段,即:SELECT具體的字段名稱,而不是 SELECT * 選擇所有字段,這樣可以減少排序區的使用,提高SQL性能。

優化group by語句

默認情況下,MySQL對所有GROUP BY col1,col2,…的字段進行排序。這與在查詢中指定ORDER BY col1,col2,…類似。因此,如果顯示包括一個相同列的order by子句,則對MySQL的實際執行性能沒有什麼影響。

如果查詢包括GROUP BY但用戶想要避免排序結果的消耗,則可以指定ORDER BY NULL禁止排序。

案例:

優化嵌套查詢

使用子查詢可以一次性地完成很多邏輯上需要多個步驟才能完成的 SQL 操作,同時也可以避免事務或者表鎖死,並且寫起來也很容易。但是,有些情況下,子查詢可以被更有效率的連接(JOIN)替代。

連接(JOIN)之所以更有效率一些,是因爲MySQL不需要在內存中創建臨時表來完成這個邏輯上需要兩個步驟的查詢工作。

優化分頁查詢

一般分頁查詢時,通過創建覆蓋索引能夠比較好地提高性能。一個常見又非常頭痛的分頁場景是"limit 1000, 20",此時MySQL排序出前1020條記錄後僅僅需要返回第1001到1020條記錄,前1000條記錄都會被拋棄,查詢和排序的代價非常高。

第一種優化思路

在索引上完成排序分頁的操作,最後根據主鍵關聯回原表查詢所需要的其他列內容。

第二種優化思路

把 limit 查詢轉換成某個位置的查詢

select ... where id < ? limit 10

注意,這樣把 LIMIT m,n 轉換成 LIMIT n 的查詢,只適合在排序字段不會出現重複值的特定環境,能夠減輕分頁翻頁的壓力;如果排序字段出現大量重複值,而仍進行這種優化,那麼分頁結果可能會丟失部分記錄,不適用這種方式進行優化。

巧用 Rand() 提取隨機行

大多數數據庫都會提供產生隨機數的包或者函數,通過這些包或者函數可以產生用戶需要的隨機數,也可以用來從數據表中抽取隨機產生的記錄,這對一些抽樣分析統計是非常有用的。

如:SELECT * FROM crm_user ORDER BY RAND() LIMIT 11

數據庫名、表名大小寫問題

在大多數UNIX環境中,由於操作系統對大小寫的敏感性導致了數據庫名和表名對大小寫敏感性,而在 Windows中,由於操作系統本身對大小寫不敏感,因此在Windows下的MySQL數據庫名和表名對大小寫也不敏感。

列、索引、存儲子程序和觸發器名在任何平臺上對大小寫不敏感。默認情況下,表別名在UNIX中對大小寫敏感,但在Windows或Mac OS X中對大小寫不敏感。如,在Linux中執行以下sql會報錯。

mysql> select * from t as test where Test.a = 1;
ERROR 1054 (42S22): Unknown column 'Test.a' in 'where clause'

 

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