第四章 查詢性能優化


    最優的表設計+最好的索引+合理的查詢設計 是高性能的三個必備條件。

一、爲什麼查詢會慢
    查詢的速度快慢,重要的是響應時間,如果把查詢看作一個任務,那麼它是又很多子任務組成,優化查詢就是優化其子任務,要麼消除一些子任務、要麼減少子任務執行次數、要麼讓子任務執行更快。
    查詢的生命週期大致是:從客戶端,到服務器,然後在服務器上進行解析,生成執行計劃,執行,並返回結果給客戶端。其中‘執行’可以認爲是生命週期中最重要的階段,這其中包含了大量爲了檢索數據到存儲引擎的調用,以及調用後的數據處理,包括排序、分組等。
    完成這些任務時,查詢需要在不同地方花費時間,包括網絡、CPU計算、生成統計信息和執行計劃、鎖等待等操作,尤其是向底層存儲引擎檢索數據的調用操作,這些操作需要在內存中操作,CPU操作以及內存不足會導致I/O操作上消耗時間,根據引擎不同,還可能是產生大量上下文切換以及系統調用。
    每一個消耗大量時間的查詢案例中,我們都能看見一些不必要的額外操作,某些操作執行了多次,某些操作執行的太慢。優化查詢的目的就是減少和消除這些操作所花費的時間。
    
二、慢查詢基礎:優化數據訪問
    查詢效率低最基本的原因就是訪問數據量太多,除了某些不可避免的情況,大部分性能低下的查詢都可以通過減少訪問數據量的方式進行優化。對於低效查詢,可以通過下面兩步驟分析很有效:
        1.確認應用程序是否在檢索大量超過需要的數據,這通常意味着訪問了太多的行,但有時也可能是訪問太多的列。
        2.確認MYSQL服務器層是否在分析大量超過需要的數據行。

是否向數據庫請求了不需要的數據
    1.查詢不需要的數據:比如用SELECT語句查詢大量的結果,然後再獲取前面N行數據關閉結果集。解決方式是查詢需要條數N,SQL語句用limit N
    2.多表關聯查詢返回全部的列:返回多個表的全部數據列是不好的,正確的是隻取需要的列。
    3.總是取出全部列:SELECT * 需要考慮是否必要,取出全部列會讓優化器無法完成索引覆蓋掃描這類優化,還會帶來額外I/O、CPU、內存消耗。如果考慮代碼複用性,查詢全部也是可以考慮的,如果應用程序採用了緩存或別的考慮。可以考慮查詢超出需要的數據列。
    4.重複查詢相同的數據:不斷重複的執行相同的查詢,然後每次返回結果都相同,可以考慮將數據緩存起來,這樣性能更好。

MYSQL是否在掃描額外的記錄
    對於mysql,最簡單的查詢開銷的三個指標如下:響應時間、掃描的行數、返回的行數。這三個數據都會記錄到慢查詢中,所以查詢慢查詢是找出掃描行數過多的查詢好方法。
    
    1.響應時間:響應時間是個表面的值,響應時間是:服務時間+排隊時間。排隊時間是指服務器因爲等待資源沒有真正執行查詢的時間,當看到一個響應時間時,需要問問自己這個值是否是個合理的值。
    2.掃描的行數和返回的行數:分析查詢時,查看該查詢的掃描行數是非常有幫助的,一定程度可以說明查詢效率高不高,掃描的行數和查詢的行數一般在1:1~10:1之間。
    3.掃描的行數和返回的類型:評估查詢開銷的時候,需要考慮從表中找出一行數據的成本。在EXPLAIN語句中,type列反應了訪問類型。訪問類型有很多種,從全表掃描到索引掃描、範圍掃描、唯一索引掃描、常數引用等,這些訪問是從慢到快的。掃描行數也是從大到小。
        如果查詢沒有辦法找到合適的訪問類型,那麼解決的最好辦法是增加一個合適的索引。一般來說,MYSQL能夠使用三種方式應用where查詢條件,從好到壞依次爲:
            Ⅰ.在索引中使用where條件來過濾不匹配的記錄,這是在存儲引擎層完成的。
            Ⅱ.使用索引覆蓋掃描(Extra列中出現Using index)來返回記錄,直接從索引中過濾不需要的記錄並返回命中結果。這是在MySQL服務器層完成的,但無需再回表查詢記錄。
            Ⅲ.從數據表中返回數據,然後過濾不滿足條件的記錄(Extra列中出現Using where),這是在MySQL服務器層完成的,MySQL需要先從數據表讀出記錄然後過濾。

三、重構查詢的方式
    優化查詢,可以轉換查詢寫法,但是性能更好。也可以修改應用代碼,以另一種方式完成查詢。最終目的是一樣的。

    1.一個複雜查詢還是多個簡單查詢:MySQL從設計上讓連接和斷開都很輕量級,再返回一個小的查詢結果方面很高效,把一個複雜的查詢分解爲多個簡單查詢是很有必要的。

    2.切分查詢:將大查詢切分爲小查詢,每個查詢功能一樣,只完成一小部分,每次返回一小部分查詢結果(分頁)

    3.分解關聯查詢:很多高性能應用都會對關聯查詢進行分解,原本一條關聯查詢分解爲多個簡單查詢,這種方式有如下優勢:
        Ⅰ.讓緩存更高效,很多簡單查詢可能已被緩存,這樣查詢會跳過訪問數據庫,關聯查詢如果其中一個表發生變化,緩存失效概率較大。
        Ⅱ.查詢分解後,執行單個查詢可以減少鎖的競爭
        Ⅲ.應用層做分解,容易對數據庫進行拆分,更容易做到高性能和可擴展。
        Ⅳ.可以減少冗餘查詢,在應用層做關聯,意味着某條記錄只需查一次,而數據庫關聯,則可能還要重複訪問一部分數據。
        Ⅴ.這樣做相當於在引用中做哈希關聯,而不是MySQL的嵌套關聯。
    4.

四、查詢執行的基礎
    當我們向MySQL發送一條請求,MySQL做了什麼:

    Ⅰ.客戶端發送一條查詢給服務器
    Ⅱ.服務器先查詢緩存,如果命中,則立刻返回存儲在緩存中的結果,否則進入下一階段。
    Ⅲ.服務器進行SQL解析、預處理、再由優化器生成對應的執行計劃。
    Ⅳ.MySQL根據優化器生成的執行計劃,調用存儲引擎API來執行查詢
    Ⅴ.將結果返回客戶端

MySQL客戶端/服務器通信協議
    在任意時刻,要麼是服務器向客戶端發送消息,要麼是客戶端向服務器發送消息,兩個動作不能同時執行。這種通信協議簡單快速,但是也有限制,一個明顯的限制就是沒法進行流量控制。
查詢緩存
    在解析一個查詢語句前,如果查詢緩存是打開的,那麼MySQL會優先檢查這個查詢是否命中緩存中的數據。如果查詢恰好命中了緩存,那麼在返回結果之前MySQL會檢查一次用戶權限,如果沒問題,返回結果給客戶端。
查詢優化處理
    查詢優化器執行一個計劃,有下面幾個步驟:
    
    Ⅰ.語法解析和預處理:語法解析器先解析語法關鍵字正確性以及關鍵字順序是否正確。預處理器進一步檢查解析樹是否合法,比如檢查表或者列名是否存在,是否歧義。
    Ⅱ.查詢優化器:一條查詢語句可能有多個執行方式,查詢優化器作用是找到最優解。
    Ⅲ.數據和索引的統計信息:MySQL系統架構中,服務器層有查詢優化器,卻沒有保存數據和索引的統計信息。統計信息由存儲引擎實現。因爲服務層沒有統計信息,所以MySQL查詢優化器在生成查詢的執行計劃時,需要向存儲引擎獲取相應信息。
        包括:每個表或者每個索引有多少個頁面、每個表每個索引基數是多少,數據行和索引長度、索引的分佈信息等。優化器根據這些信息來選擇一個最優的執行計劃。
    Ⅳ.執行計劃:MySQL不會如其它數據庫一樣生成查詢字節碼來執行,而是生成指令樹,通過查詢引擎執行指令樹並返回結果。
查詢執行引擎
    在解析和優化階段,MySQL將生成查詢對應的執行計劃,MySQL的查詢引擎則根據這個計劃來完成整個查詢
返回結果給客戶端
    查詢的最後一步是返回結果,即使不需要返回結果集給客戶端,MySQL仍然會返回這個查詢的一些信息,如查詢影響的行數。如果查詢可被緩存,這一步還會將結果放在查詢緩存中。
    

五、MYSQL查詢優化器的侷限性

    關聯子查詢:MySQL的子查詢實現很糟,尤其是where條件包含in()的子查詢。
    UNION的限制:UNION關鍵字的實現不好。
    索引合併優化:
    等值傳遞:某些時候,等值傳遞會帶來額外消耗。
    並行執行:MySQL無法利用多核特性來執行並行查詢。
    哈希關聯:MySQL不支持哈希關聯。
    鬆散索引掃描:MySQL不支持鬆散索引掃描,也就是無法按照不連續的方式掃描一個索引。
    最大值最小值優化:對於MIN()、MAX()優化做的並不好
    在同一張表上查詢和更新:MySQL不支持對一張表同時查詢和更新。

六、優化特定類型的查詢
優化COUNT()查詢:COUNT可以統計某個列值的數量,也可以統計結果集的行數。統計列值數量時,要求列值是非空的(不統計NULL)。統計結果集行數,使用count(*),它會忽略列直接統計行數。
優化關聯查詢:1.確保ON或者USING字句的列上有索引。2.確保任何GROUP BY和ORDER BY中的表達式只涉及一個表中的列。3.升級MySQL時需要注意,關聯語法、運算符優先級等可能發生變化的地方。
優化子查詢:儘量用關聯查詢代替子查詢,如果MySQL5.6以後的版本,可以忽略子查詢的建議。
優化GROUP BY 和 DISTINCT:這兩種查詢都可以使用索引優化,這是也最有效的優化方案。

總結:理解查詢是如何執行以及時間都消耗到哪些地方,再加上一些諸如解析和優化過程的知識,可以進一步理解上一章討論的MySQL如何訪問表和索引的內容,從另一個維度幫助讀者理解MySQL在訪問表和索引時查詢和索引的關係。
    優化通常需要三管齊下:不做、少做、快速的做。除了這些基礎手段,包括查詢、表結構、索引等,MySQL還有一些高級特性可以幫助優化應用,例如分區、分區和索引類似但是原理不同。MySQL還支持查詢緩存,它可以幫
    你緩存查詢結果,當執行完全相同的查詢時,直接使用緩存技術。下一章會介紹這些特性。

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