查詢性能優化算法
1. 爲何查詢速度會慢?數據庫
1). 若是把查詢看做是一個任務,那麼它由一系列子任務組成,每一個子任務都會消耗必定的時間。若是要優化查詢,實際上要優化其子任務,要麼消除其中一些子任務,要麼減小子任務的執行次數,要麼讓子任務運行的更快。緩存
2). 一般來講,查詢的生命週期大體能夠按照順序來看:從客戶端,到服務器端,而後在服務器上進行解析,生成執行計劃,執行,並返回結果給客戶端。其中"執行"能夠認爲是整個生命週期中最重要的階段,這其中包括性能優化
大量爲了檢索數據到存儲引擎的調用以及調用後的數據處理,包括排序、分組等。服務器
3). 在完成這些任務的時候,查詢須要在不一樣的地方花費時間,包括網絡,CPU計算,生成統計信息和執行計劃、鎖等待(互斥等待)等操做,尤爲是向底層存儲引擎檢索數據的調用操做,這些調用須要在內存中操做、CPU操做網絡
和內存不足時致使的IO操做上消耗時間,根據上下文不一樣,可能會產生大量的上下文切換以及系統調用。數據結構
2. 慢查詢基礎:優化數據訪問併發
查詢性能低下最基本的緣由是訪問的數據太多。某些查詢可能不可避免地須要篩選大量數據,但這並不常見。大部分性能低下的查詢均可以經過減小訪問的數量的方式進行優化。函數
對於低效查詢,能夠經過下面兩個步驟來分析:性能
1). 確認應用程序是否在檢索大量超過須要的數據。這一般意味着訪問了太多的行,但有時候多是訪問了太多的列。
2). 確認MySQL服務器層是否在分析大量超過須要的數據行。
2.1 是否向數據庫請求了不須要的數據
1). 一些典型案例
a. 查詢不須要的記錄:一個常見的錯誤是經常會誤覺得MySQL會只返回須要的數據,實際上MySQL倒是返回所有結果集在進行計算。最簡單有效的解決方法是在這樣的查詢後面加上LIMIT。
b. 多表關聯時返回所有列
c. 老是取出所有列:每次看到SELECT * 的時候都須要用懷疑的眼光審視,是否是真的須要返回所有列?取出所有列會讓優化器沒法完成索引覆蓋掃描這類優化,還會爲服務器帶來額外的網絡、IO、內存和
CPU的消耗。
d. 重複查詢相同的數據:比較好的方案是,當初次查詢的時候將這個數據緩存起來,須要的時候從緩存中取出,這樣性能會更好。
2.2 MySQL是否在掃描額外的記錄:
1). 對於MySQL,最簡單的衡量查詢開銷的三個指標以下:
a. 響應時間:響應時間是兩部分之和:服務時間和排隊時間。服務時間是指數據庫處理這個查詢真正花多長時間。排隊時間是指服務器由於等待某些資源而沒有真正執行查詢的時間--多是等IO操做完成,也可能
是等待行鎖等等。
b. 掃描的行數和返回的行數:分析查詢時,查看該查詢掃描的行數是很是有幫助的。這在必定程度上可以說明該查詢找到須要的數據的效率高不高。
c. 掃描的行數和訪問類型:在評估查詢開銷的時候,須要考慮一下從表中找到某一行數據的成本。MySQL有好幾種訪問方式能夠查詢並返回一行結果。有些方式可能須要掃描不少行才能返回一行結果,也有些訪問
方式可能無需掃描就能返回結果。
在EXPALIN語句中的type列反應了訪問類型。訪問類型有不少種,從全表掃描到索引掃描、範圍掃描、惟一索引掃描、常數引用等。這裏列的這些,速度是從慢到快,掃描的行數是從多到少。你不要記住這
些訪問類型,但須要明白掃描表、掃描索引、範圍訪問和單值訪問的概念。
2). 通常MySQL能使用以下三種方式應用WHERE條件,從好到壞依次爲:
a. 在 索引 中使用WHERE條件來過濾不匹配的記錄。這是在存儲引擎層完成的。
b. 使用索引覆蓋掃描(在Extra列中出現Using index)來返回記錄,直接從索引中過濾不須要的記錄並返回命中的結果。這是在MySQL服務器層完成的,但無需再回表查詢記錄。
c. 從數據表中返回數據,而後過濾掉不知足條件的記錄(在Extra列中出現Using Where)。這在MySQL服務器層完成,MySQL須要先從數據表讀取記錄而後過濾。
3). 若是發現查詢須要掃描大量的數據但只返回少數的行(使用聚合函數等),那麼一般能夠嘗試下面的技巧去優化它們:
a. 使用索引覆蓋掃描,把全部須要用的列都放到索引中,這樣存儲引擎無需回表獲取對應行就能夠返回結果了。
b. 改變庫表結構。例如使用單獨的彙總表。
c. 重寫這個複雜的查詢,讓MySQL優化器可以以更優的方式執行這個查詢。
3. 重構查詢的方式:有時候,能夠將查詢轉換一種寫法讓其返回同樣的結果,但性能更好。
3.1 一個複雜查詢仍是多個簡單查詢
a. 設計查詢的時候一個須要考慮的重要問題是,是否須要將一個複雜的查詢分紅過個簡單的查詢。在傳統實現中,老是強調須要數據庫層完成儘量多的工做,這樣作的邏輯在於之前老是認爲網絡通訊、
查詢解析和優化是一件代價很高的事情。可是這樣的想法對於MySQL並不適用,MySQL從設計上讓鏈接和斷開鏈接都很輕量級,在返回一個小的查詢結果方面很高效。現代的網絡速度比之前要快的多,
不管是帶寬仍是延遲。
b. MySQL內部每秒可以掃描內存中上百萬行數據,相比之下,MySQL響應數據給客戶端就慢得多了。在其餘條件都相同的時候,使用盡量少的查詢固然是更好的。可是有時候,將一個大查詢分解成
多個小查詢也是頗有必要的。
3.2 切分查詢:刪除舊數據是一個很好的例子。按期清除大量數據時,若是用一個大的語句一次性刪除完成的話,則可能須要一次鎖住不少數據、佔滿整個事務日誌、耗盡系統資源、阻塞不少小的但很重要的查詢。
同時須要注意,若是每次刪除數據後,都暫停一會再作下一次刪除,能夠經服務器壓力分散到很長的時間段中。
3.3 分解關聯查詢:
分解關聯查詢的方式重構查詢有以下的優點:
a. 讓緩存的效率更高。許多應用程序能夠方便地使用緩存單表查詢對應的結果集。
b. 將查詢分解後,執行單個查詢能夠減小鎖的競爭。
c. 在應用層作關聯,能夠更容易對數據庫進行拆分,更容易作到高性能和可擴展性。
d. 查詢自己效率也可能會有所提高。
e. 能夠減小冗餘記錄的查詢。管理查詢中可能須要重複地訪問一部分數據。
f. 更進一步,這樣作至關於在應用中實現了哈希關聯,而不是使用MySQL的嵌套循環關聯。某些場景哈希關聯的效率要高不少。
4. 查詢執行的基礎:
查詢執行的過程:
1). 客戶端發送一條查詢給服務器
2). 服務器檢查查詢緩存,若是命中了緩存,則馬上返回存儲在緩存中的結果。不然進入下一階段。
3). 服務器端進行SQL解析、預處理,再由優化器生成對應的執行計劃。
4). MySQL根據優化器生成的執行計劃,調用存儲引擎的API來執行查詢。
5). 將結果返回給客戶端。
4.1 MySQL客戶端/服務器通訊協議:MySQL客戶端和服務器之間的通訊協議是"半雙工"的,這意味着,在任何一個時刻,要麼是由服務器向客戶端發送數據,要麼是由客戶端向服務器發送數據,這兩個動做不能
同時發生,因此咱們也沒法將一個消息切換成小塊獨立來發送。
1). MySQL一般要等待全部的數據都已經發送給客戶端才能釋放這條查詢所佔的資源,因此接收所有結果並緩存一般能夠減小服務器的壓力(?),讓查詢可以早點結束、早點釋放相應的資源。
2). 查詢狀態:對於一個MySQL鏈接,或者說一個線程,任什麼時候刻都有一個狀態,該狀態表示了MySQL當前正在作什麼。有不少種方式能查看當前的狀態,最簡單的是使用SHOW FULL PROCESSLIST命令(
該命令返回結果中的Command列就表示當前的狀態)。下面將這些狀態列出來,並作一個簡單的解釋:
a. Sleep:線程正在等待客戶端發送新的請求。
b. Query:線程正在執行查詢或者正在將結果發送給客戶端。
c. Locked:在MySQL服務器層,該線程正在等待表鎖。
d. Analyzing and statistics : 線程正在收集存儲引擎的統計信息,並生成查詢的執行計劃。
e. Coping to tmp table [on disk]:線程正在執行查詢,而且將其結果都複製到一個臨時表中,這種狀態通常要麼是在作GROUP BY操做,要麼是文件排序操做,或者是UNION操做。若是這個狀態後面
還有"on disk"標記,那表示MySQL正在講一個內存臨時表放到磁盤上。
f. Sorting result:線程正在對結果集進行排序。
g. Sending data:這表示多種狀況:線程可能在對多個狀態之間傳輸數據,或者而在生成結果集,或者在向客戶端返回數據。
4.2 查詢緩存:在解析一個查詢語句以前,若是查詢緩存時打開的,那麼MySQL會優先檢查這個查詢是否命中查詢緩存中的數據。這個檢查是經過一個大小寫敏感的哈希查找實現的。查詢和緩存中的查詢即便只有
一個字節不一樣,那也不會匹配緩存結果,這種狀況下查詢會進入下一個階段處理。
4.3 查詢優化處理:這個階段包括多個子階段:解析SQL、預處理、優化SQL查詢計劃。這個過程當中任何錯誤(例如語法錯誤)均可能終止。
a. 語法解析器和預處理:MySQL經過關鍵字將SQL語句進行解析,並生成一顆對應的"解析樹",MySQL解析器將使用MySQL語法規則驗證和解析查詢。預處理則根據一些MySQL規則進一步檢查解析樹是否合法。
b. 查詢優化器:一條查詢語能夠有不少執行方式,最後都返回相同的結果。優化器的做用是找到這其中最好的執行計劃。MySQL使用基於成本的優化器,它將嘗試預測一個查詢使用某種執行時的成本,並選擇其中
成本最小的一個。
1). 有不少種緣由會致使MySQL優化器選擇錯誤的執行計劃,以下所示:
a. 統計信息不許確。MySQL依賴存儲引擎提供的統計信息來評估成本,可是有的存儲引擎提供的信息時不許確的,有的誤差可能很是大。例如:InnoDB由於其MVCC的機構,並不維護一個數據表的行數的精確信息。
b. 執行計劃中的成本估算不等同於實際執行的成本。
c. MySQL的最優可能和你想的最優不同。你可能但願執行時間儘量的短,可是MySQL只是基於其成本模型選擇最優的執行計劃,而有些時候付這並非最快的。
d. MySQL從不考慮其餘併發執行的查詢,這可能會影響到當前的查詢速度。
f. MySQL也並非任什麼時候候都是基於成本的優化。
g. MySQL不會考慮不受其控制的操做成本,例如執行存儲過程或者用戶自定義函數的成本。
h. 優化器有時候沒法去估算全部可能的執行計劃,因此他可能錯估實際上最優的執行計劃。
2). 優化策略能夠簡單的分爲兩種:一種靜態優化,一種動態優化。
a. 靜態優化能夠直接對解析樹進行分析,並完成優化。靜態優化在第一次完成後就一直有效,即便使用不一樣的參數重複執行也不會發生變化。能夠認爲這是一種"編譯時優化"。
b. 動態優化則和查詢的上下文有關,也可能和不少其餘因素有關。例如WHERE條件中的取值、索引中條目對應的數據行數等。須要在每次查詢的時候從新評估,能夠認爲是一種"運行時優化"。
c. MySQL對查詢的靜態優化只須要作一次,但對查詢的動態優化則在每次執行時都須要從新評估。有時候甚至在查詢的執行過程當中也會從新優化。
3). 下面是一些MySQL可以處理的優化過程:
a. 從新定義關聯表的順序:
b. 將外鏈接轉換爲內鏈接
c. 使用等價變化規則:MySQL可使用一些等價變化來簡化並規範表達式。它能夠合併和減小一些比較,還能夠移除一些恆成立和一些恆不成立的判斷。例如:(5=5 ADN a>5) --> a>5。
d. 優化COUNT(),MIN()和MAX():索引和列是否可爲空一般能夠幫助MySQL優化這類表達式。例如:要找到某一列的最小值,只須要查詢對應B-Tree索引最左端的記錄,MySQL能夠直接獲取索引的最小行。
e. 預估並轉化爲常數表達式:當MySQL檢測到一個表達式能夠轉化爲常數的時候,就會一直把該表達式做爲常數進行優化處理。
f. 覆蓋索引掃描
g. 子查詢優化
h. 提早終止查詢:在發現已知足查詢需求的時候,MySQL老是可以馬上終止查詢。一個典型的例子是使用LIMIT。
i. 等值傳播:USING(film_id)
j. 列表IN()的比較:在不少數據系統中,IN()徹底等同於多個OR條件的子句,由於這二者是徹底等價的。在MySQL中這點是不成立的,MySQL將IN()列表中的數據先進行排序,而後經過二分查找的方式來肯定
列表中的值是否知足條件,這是一個O(log n)複雜度的操做,等價地轉換成OR查詢的複雜度爲O(n),對於IN()列表中有大量取值的時候,MySQL的處理速度將會更快。
4). 數據和索引的統計信息:由於服務器層沒有任何統計信息,因此MySQL查詢優化器在生成查詢的執行計劃時,須要向存儲引擎獲取相應的統計信息。
5). MySQL如何執行關聯查詢:當前MySQL關聯執行的策略很簡單:MySQL對任何關聯都執行嵌套循環操做,即MySQL先在一個表中循環取出單條數據,而後再嵌套循環到下一個表中尋找匹配的行,依次下去,直
到全部表中匹配的行爲止。而後根據各個表匹配的行,返回查詢中須要的各個列。MySQL會嘗試在最後一個關聯表中找到全部匹配的行,若是最後關聯表沒法找到更多的行之後,MySQL返回到上一層次關聯表,
看是否可以找到更多匹配記錄,以此類推迭代執行。
6). 關聯查詢優化器:MySQL優化器最重要的一部分就是關聯查詢優化,它決定了多個表關聯時順序。一般多表關聯的時候,能夠有多種不一樣的關聯順序來得到相同的執行結果。關聯查詢優化器則經過評估不一樣的順序
時的成原本選擇一個代價最小的關聯順序。優化器會將數據量少的表先進行查詢(我的認爲第一個查詢的表越小,臨時表就越小?嵌套查詢,減小查詢次數?)。
7). 排序優化:不管如何排序都是一個成本很高的操做,因此從性能角度考慮,應儘量避免排序或者儘量避免對大量數據進行排序。儘可能經過索引進行排序。當不能使用索引生成排序結果的時候,MySQL須要本身
進行排序,若是數據量小則在內存中進行,若是數量大則須要使用磁盤,不過MySQL將這個過程統一稱爲文件排序,即便徹底是內存排序不須要任何磁盤文件時也是如此。
MySQL有以下兩種排序算法:
a. 兩次傳輸排序(舊版本使用):讀取行指針和須要排序的字段,對其進行排序,而後再根據排序結果讀取所須要的數據行。須要進行兩次傳輸,即須要從數據表中讀取兩次數據,第二次讀取數據的時候,由於是讀
取排序列進行排序後的全部記錄。這回產生大量的隨機IO。
b. 單次傳輸排序(新版本使用):先讀取查詢所須要的全部列,而後在根據給定列進行排序,最後直接返回排序結果。效率更高,但佔用內存更大。
若是查詢中有LIMIT的話,LIMIT也會在排序以後應用的,因此即便須要返回較少的數據,臨時表和須要排序的數據量仍而後很是大。貌似5.6版本有所改進,會先拋棄不知足條件的記錄,而後再進行排序。
4.4 查詢執行引擎:在解析和優化階段,MySQL將生成查詢對應的執行計劃,MySQL的查詢執行引擎則根據這個執行計劃來完成整個查詢。這裏執行計劃是一個數據結構,而不是和不少其餘的關係型數據庫那樣會
生成對應的字節碼。
4.5 返回結果給客戶端:MySQL將結果集返回客戶端是一個增量、逐步返回的過程。開始生成第一條結果時,MySQL就開始向客戶端逐步返回結果集了。
5. MySQL查詢優化器的侷限性:
5.1 關聯子查詢:MySQL的子查詢實現很是糟糕(5.6版本之後有改進)。最糟糕的一類查詢是WHERE條件中包含IN()的子查詢語句。
1). 由於使用IN()加子查詢,性能常常會很是糟,因此一般建議使用EXISTS()等效的改寫查詢來獲取更好的效率。
2). 通常建議使用左外鏈接(LEFT OUTER JOIN)代替子查詢(?)。
5.2 UNION的限制:MySQL沒法將限制條件從外層"下推"到內層,這使得本來可以限制部分返回結果的條件沒法應用到內層查詢的優化上。
例如若是但願UNION的各個子句可以根據LIMIT只去部分結果集,或者但願可以先排好序再合併結果集的話,就須要在UNION的各個子句中分別使用這些語句。
(SELECT first_name,last_name FROM sakila.actor ORDER BY last_name) UNION ALL (SELECT first_name ,last_name FROM sakila.customer ORDER BY last_name) LIMIT 20;
優化後:
(SELECT first_name,last_name FROM sakila.actor ORDER BY last_name LIMIT 20) UNION ALL (SELECT first_name ,last_name FROM sakila.customer ORDER BY last_name LIMIT 20) LIMIT 20;
5.3 當WHERE子句包含多個複雜條件的時候,MySQL可以訪問單個表的多個索引以合併和交叉過濾的方式來定位須要查找的行。
5.4 等值查詢:某些時候,等值查詢會帶來一些意想不到額外消耗。例如:有一個很是大的IN()列表,而MySQ優化器發現存在WHERE、ON或者USING的子句。
5.5 並行執行:MySQL沒法利用多核特性來並行執行查詢(貌似5.6之後有改進)。
5.6 哈希關聯:MySQL不支持哈希關聯。
5.7 鬆散索引掃描:MySQL並不支持鬆散索引掃描,也就沒法按照不連續的方式掃描一個索引。一般,MySQL的索引掃描須要先定義一個起點和終點,即便須要的數據只是這段索引中的不多幾個,MySQL仍需掃描這段索引中
的每個條目。
5.8 最大值和最小值優化:對於MIN()和MAX()查詢,MySQL的優化作的並很差。例如:
SELECT MIN(actor_id) FROM sakila.actor WHERE first_name='PENELOPE'
由於first_name上沒有索引,因此會進行全表掃描。若是MySQL可以進行主鍵掃描,那麼理論上,當MySQL讀到第一個知足條件的記錄的時候,就是咱們須要的最小值,由於主鍵是嚴格按照actor_id大小字段排序的。
一個曲線優化的辦法是移除MIN(),而後使用LIMIT來將查詢重寫。
5.9 在同一個表上查詢和更新:MySQL不容許對同一張表同時進行查詢和更新。
6. 查詢優化器的提示(hint):若是對優化器選擇的執行計劃不滿意,可使用優化器提供的幾個提示(hint)來控制最終的執行計劃。
7. 優化特定類型的查詢
7.1 優化COUNT()查詢
1). COUNT()是一個特殊的函數,有兩種很是不一樣的做用:它能夠統計某個列值的數量,也能夠統計行數。在統計列值的時候要求列值是非空的(不統計NULL)。若是COUNT()的括號中指定了列或者列的表達式,則
統計的就是這個表達式有值的結果數。最簡單的就是咱們使用count(*)的時候,這種狀況下通配符*並不會向咱們猜測的那樣擴展全部的行,實際上,它會忽略全部的值而直接統計全部的行數。
2). 使用近似值:有時候某些業務場景並不要求徹底精確的COUNT值,此時能夠用近似值來代替。
3). 更復雜的優化:覆蓋索引,增長彙總表等。
7.2 優化關聯查詢:
1). 確保ON或者USING子句中的列上有索引。在建立索引的時候就要考慮到關聯的順序。當表A和表B用到列C關聯的時候,若是優化器關聯順序是B、A,那就不須要在B表的對應列上創建索引。沒有用到的索引只會
帶來額外的負擔。通常來講,除非有其餘理由,不然只須要在關聯順序中的第二個表的相應列上建立索引。
2). 確保任何的GROUP BY 和ORDER BY中的表達式只涉及到一個表中的列。這樣MySQL纔有可能使用索引來優化這個過程。
7.3 優化子查詢:關於優化子查詢咱們給出的最重要的優化建議就是儘量使用關聯查詢代替,至少當前MySQL版本須要這樣。
7.4 優化GROUP BY和DISTINCT:
1). 它們均可以使用索引來優化,這也是最有效的方法。
2). 在MySQL中,當沒法使用索引的時候,GROUP BY使用兩種策略來完成:使用臨時表或文件排序來作分組。對於任何查詢語句,這兩種策略的性能都有能夠提高的地方。能夠經過使用提示SQL_BIG_RESULT和
SQL_SMALL_RESULT來讓優化器按你但願的方式運行。
3). 若是須要對關聯查詢分組(GROUP BY),而且是按照查找表中的某個列進行分組,那麼一般採用查找表的標識列分組的效率比其餘列更高。
4). 若是沒有經過ORDER BY子句顯式地指定排序列,當查詢使用GROUP BY 子句的時候,結果集會自動按照分組的列進行排序。若是不關心結果集的順序,而這中默認排序又致使了須要文件排序,則可使用
ORDER BY NULL,讓MySQL文件再也不進行排序。也能夠在GROUP BY子句中直接使用DESC或者ASC關鍵字,使分組的結果集按照須要的方向排序。
5). 優化GROUP BY WITH ROLLUP:分組查詢的一個變種思想就是要求MySQL對返回的分組結果再作一次超級聚合。最好的辦法儘量的將WITH ROLLUP 功能轉移到應用程序中處理。
7.5 優化LIMIT分頁:
1). 使用索引
2). 要優化這種查詢,要麼是在頁面中限制分頁的數量,要麼是優化大偏移量的性能。
3). 盡肯能的使用索引覆蓋
4). 延遲關聯
5). 有時候也能夠將LIMIT查詢轉換爲已知位置的查詢,讓MySQL經過範圍掃描找到對應的結果。
6). 其餘優化辦法還包括使用預先計算的彙總表,或者關聯一個冗餘表,冗餘表只包含主鍵列和須要作排序的數據列。
7.6 優化SQL_CALC_FOUND_ROWS:分頁的時候,另外一個經常使用的技巧是在LIMIT語句中加上SQL_CALC_FOUND_ROWS提示(hint),這樣就能夠得到去掉LIMIT以知足條件的行數,所以能夠做爲分頁的總數。
用業務的手段解決:下一頁,獲取更多數據等。
7.7 優化UNION查詢:
1). MySQL老是經過建立填充臨時表的方式來執行UNION查詢。所以不少優化策略在UNION查詢中都無法很好地使用。常常須要手工地將WHERE,LIMIT,ORDER BY等子句"下推"到UNION的各個子查詢中,以
便優化器能夠充分利用這些條件進行優化。
2). 除非確實須要服務器消除重複的行,不然就必定要使用UNION ALL,這一點很重要。若是沒有ALL關鍵字,MySQL會給臨時表加上DISTINCT選項,這回致使對臨時表作惟一性檢查。這樣作的代價很是高,
即便有ALL關鍵字,MySQL仍然會使用臨時表存儲結果。事實上,MySQL老是經結果放入臨時表,而後再讀出,再返回給客戶端。
7.8 靜態查詢分析:Percona Toolkit中的pt-query-advisor 可以解析查詢日誌、分析查詢模式,而後再給出全部可能存在的潛在問題的查詢,並給出足夠詳細的建議。這像是給MySQL全部的查詢作一次全面的健康
檢查,它能檢測出不少問題。
7.9 用戶自定義變量:
8. 通常,咱們要儘可能避免使用SELECT_FOR_UPDATE。不光是隊列表,任何狀況下都要儘可能避免。
9. 須要處理一種特殊的狀況:那些正在被進程處理,而進程自己卻因爲某種緣由退出的狀況。這種狀況處理起來很簡單。你只須要按期運行UPDATE語句將它都更新成原始狀態就能夠了,而後執行SHOW PROCESSLIST,
得到當前正在工做的線程ID,並使用一些WHERE條件避免取到那些剛開始處理的進程。
1. 是否向數據庫請求了不須要的數據
a. 查詢不須要的記錄:一個常見的錯誤是經常會誤覺得MySQL會只返回須要的數據,實際上MySQL倒是返回所有結果集在進行計算。最簡單有效的解決方法是在這樣的查詢後面加上LIMIT。
b. 多表關聯時返回所有列
c. 老是取出所有列:每次看到SELECT * 的時候都須要用懷疑的眼光審視,是否是真的須要返回所有列?取出所有列會讓優化器沒法完成索引覆蓋掃描這類優化,還會爲服務器帶來額外的網絡、IO、內存和CPU的消耗。
d. 重複查詢相同的數據:比較好的方案是,當初次查詢的時候將這個數據緩存起來,須要的時候從緩存中取出,這樣性能會更好。
2. MySQL是否在掃描額外的記錄:
1). 通常MySQL能使用以下三種方式應用WHERE條件,從好到壞依次爲:
a. 在索引中使用WHERE條件來過濾不匹配的記錄。這是在存儲引擎層完成的。
b. 使用索引覆蓋掃描(在Extra列中出現Using index)來返回記錄,直接從索引中過濾不須要的記錄並返回命中的結果。這是在MySQL服務器層完成的,但無需再回表查詢記錄。
c. 從數據表中返回數據,而後過濾掉不知足條件的記錄(在Extra列中出現Using Where)。這在MySQL服務器層完成,MySQL須要先從數據表讀取記錄而後過濾。
3). 若是發現查詢須要掃描大量的數據但只返回少數的行(使用聚合函數等),那麼一般能夠嘗試下面的技巧去優化它們:
a. 使用索引覆蓋掃描,把全部須要用的列都放到索引中,這樣存儲引擎無需回表獲取對應行就能夠返回結果了。
b. 改變庫表結構。例如使用單獨的彙總表。
c. 重寫這個複雜的查詢,讓MySQL優化器可以以更優的方式執行這個查詢。
3. 重構查詢的方式:切分查詢 ,分解關聯查詢
4. 數據庫鏈接池:預編譯
5. 優化特定類型的查詢
1). 優化COUNT()查詢
1). COUNT()是一個特殊的函數,有兩種很是不一樣的做用:它能夠統計某個列值的數量,也能夠統計行數。在統計列值的時候要求列值是非空的(不統計NULL)。若是COUNT()的括號中指定了列或者列的表達式,則
統計的就是這個表達式有值的結果數。最簡單的就是咱們使用count(*)的時候,這種狀況下通配符*並不會向咱們猜測的那樣擴展全部的行,實際上,它會忽略全部的值而直接統計全部的行數。
2). 使用近似值:有時候某些業務場景並不要求徹底精確的COUNT值,此時能夠用近似值來代替。
3). 更復雜的優化:覆蓋索引,增長彙總表等。
2). 優化關聯查詢(確保關聯查詢中可使用索引):
1). 確保ON或者USING子句中的列上有索引。在建立索引的時候就要考慮到關聯的順序。當表A和表B用到列C關聯的時候,若是優化器關聯順序是B、A,那就不須要在B表的對應列上創建索引。沒有用到的索引只會
帶來額外的負擔。通常來講,除非有其餘理由,不然只須要在關聯順序中的第二個表的相應列上建立索引。
2). 確保任何的GROUP BY 和ORDER BY中的表達式只涉及到一個表中的列。這樣MySQL纔有可能使用索引來優化這個過程。
3). 優化子查詢:關於優化子查詢咱們給出的最重要的優化建議就是儘量使用關聯查詢代替,至少當前MySQL版本須要這樣。
4). 優化GROUP BY和DISTINCT:
1). 它們均可以使用索引來優化,這也是最有效的方法。
2). 在MySQL中,當沒法使用索引的時候,GROUP BY使用兩種策略來完成:使用臨時表或文件排序來作分組。對於任何查詢語句,這兩種策略的性能都有能夠提高的地方。能夠經過使用提示SQL_BIG_RESULT和
SQL_SMALL_RESULT來讓優化器按你但願的方式運行。
3). 若是須要對關聯查詢分組(GROUP BY),而且是按照查找表中的某個列進行分組,那麼一般採用查找表的標識列分組的效率比其餘列更高。
4). 若是沒有經過ORDER BY子句顯式地指定排序列,當查詢使用GROUP BY 子句的時候,結果集會自動按照分組的列進行排序。若是不關心結果集的順序,而這中默認排序又致使了須要文件排序,則可使用
ORDER BY NULL,讓MySQL文件再也不進行排序。也能夠在GROUP BY子句中直接使用DESC或者ASC關鍵字,使分組的結果集按照須要的方向排序。
5). 優化GROUP BY WITH ROLLUP:分組查詢的一個變種思想就是要求MySQL對返回的分組結果再作一次超級聚合。最好的辦法儘量的將WITH ROLLUP 功能轉移到應用程序中處理。
5). 優化LIMIT分頁:
1). 使用索引
2). 要優化這種查詢,要麼是在頁面中限制分頁的數量,要麼是優化大偏移量的性能。
3). 盡肯能的使用索引覆蓋
4). 延遲關聯
5). 有時候也能夠將LIMIT查詢轉換爲已知位置的查詢,讓MySQL經過範圍掃描找到對應的結果。
6). 其餘優化辦法還包括使用預先計算的彙總表,或者關聯一個冗餘表,冗餘表只包含主鍵列和須要作排序的數據列。
6). 優化SQL_CALC_FOUND_ROWS:分頁的時候,另外一個經常使用的技巧是在LIMIT語句中加上SQL_CALC_FOUND_ROWS提示(hint),這樣就能夠得到去掉LIMIT以知足條件的行數,所以能夠做爲分頁的總數。
用業務的手段解決:下一頁,獲取更多數據等。
7). 優化UNION查詢:
1). MySQL老是經過建立填充臨時表的方式來執行UNION查詢。所以不少優化策略在UNION查詢中都無法很好地使用。常常須要手工地將WHERE,LIMIT,ORDER BY等子句"下推"到UNION的各個子查詢中,以
便優化器能夠充分利用這些條件進行優化。
2). 除非確實須要服務器消除重複的行,不然就必定要使用UNION ALL,這一點很重要。若是沒有ALL關鍵字,MySQL會給臨時表加上DISTINCT選項,這回致使對臨時表作惟一性檢查。這樣作的代價很是高,
即便有ALL關鍵字,MySQL仍然會使用臨時表存儲結果。事實上,MySQL老是經結果放入臨時表,而後再讀出,再返回給客戶端。
8). 靜態查詢分析:Percona Toolkit中的pt-query-advisor 可以解析查詢日誌、分析查詢模式,而後再給出全部可能存在的潛在問題的查詢,並給出足夠詳細的建議。這像是給MySQL全部的查詢作一次全面的健康
檢查,它能檢測出不少問題。
5. 是否使用查詢緩存,以及怎麼使用查詢緩存。多插入時,禁用查詢緩存
6. 批量插入