第六章 查詢性能優(yōu)化(上)

為什么查詢會慢?

響應(yīng)時間、掃描的行數(shù)、返回的行數(shù),是衡量查詢開銷的三個指標(biāo)(記錄在慢日志中)

  • 查詢了不需要的數(shù)據(jù)

    • 查詢不需要的記錄:比如查詢了100條記錄到應(yīng)用,取其中的10條顯示,最好的解決辦法是加上LIMIT
    • 多表關(guān)聯(lián)時返回全部列:應(yīng)該只取需要的列,否則會返回所有關(guān)聯(lián)表的所有記錄
    • 總是取出全部列:比如SELECT * FROM xxx
    • 重復(fù)查詢相同的數(shù)據(jù):最好使用緩存
  • 掃描了額外的記錄

    Explain語句中的type列的類型:

    • 全表掃描
    • 范圍掃描
    • 唯一索引查詢
    • 常數(shù)引用

    MySQL能夠使用三種方式應(yīng)用Where過濾條件,從好到壞依次為:

    • 在索引中使用Where條件來過濾不匹配的記錄。這是在儲存引擎層完成的
    • 使用覆蓋索引掃描(Using index)來返回記錄,直接從索引中過濾不需要的記錄并返回命中的結(jié)果。這是在MySQL服務(wù)器層,但無須再回表查詢記錄
    • 從數(shù)據(jù)表中返回數(shù)據(jù),然后過濾不滿足條件的記錄(Using where)。這是在服務(wù)器層完成,MySQL需要先從數(shù)據(jù)表讀出記錄然后過濾

怎么優(yōu)化查詢?

  • 可以將一個復(fù)雜查詢改成多個簡單查詢

    因為MySQL從設(shè)計上讓連接和斷開都非常輕量級,加上現(xiàn)代網(wǎng)絡(luò)速度非常快,多個小查詢不成問題

  • 切分查詢

    將一個大查詢,切分成小查詢,每個小查詢的功能完全一樣,每次只返回小部分查詢結(jié)果。比如刪除舊數(shù)據(jù),如果用一個很大的語句一次性刪除大量數(shù)據(jù),會鎖住很多數(shù)據(jù)、占滿事務(wù)日志、耗盡系統(tǒng)資源、阻塞很多重要的小查詢,可以切分成每次刪除1000條,循環(huán)刪除

  • 分解關(guān)聯(lián)查詢

    • 讓緩存效率更高
    • 減少鎖的競爭
    • 更容易拆分?jǐn)?shù)據(jù)庫,方便擴(kuò)展
    • 提升查詢效率,比如用IN()代替關(guān)聯(lián)查詢
    • 減少冗余記錄的查詢

一條查詢是如何執(zhí)行的?

1、客戶端發(fā)送一條查詢給服務(wù)器

MySQL客戶端和服務(wù)器之間的通信協(xié)議是“半雙工”的,同一時刻,要么是由服務(wù)器向客戶端發(fā)送數(shù)據(jù),要么是由客戶端向服務(wù)器端發(fā)送數(shù)據(jù),這兩個動作不能同時發(fā)生

2、服務(wù)器先檢查查詢緩存,如果命中緩存,則立即返回儲存在緩存中的結(jié)果。否則進(jìn)入下一階段

查詢的狀態(tài)

Sleep:線程正在等待客戶端發(fā)送新的請求

Query:線程正在查詢或者正在將結(jié)果發(fā)送給客戶端

Locked:服務(wù)器層等待標(biāo)鎖

Analyzing and statistics:線程正在收集存儲引擎的統(tǒng)計信息,并生成查詢的執(zhí)行計劃

Copying to tmp table [on disk]:將結(jié)果拷貝到一個臨時表,要么在做GROUP BY,要么文件排序、UNION表。如果后面還有“on disk”,表示MySQL正在將一個內(nèi)存臨時表放到磁盤上操作

Sorting result:線程正在對結(jié)果集進(jìn)行排序

Sending data:線程正在傳送數(shù)據(jù)

查詢緩存

解析SQL語句之前,優(yōu)先檢查查詢緩存,這是通過哈希查找實現(xiàn)的,如果命中緩存,那么在返回數(shù)據(jù)之前,還要檢查一次用戶權(quán)限

3、服務(wù)器端進(jìn)行SQL解析、預(yù)處理,再由優(yōu)化器生成對應(yīng)的執(zhí)行計劃

MySQL通過關(guān)鍵字對SQL語句進(jìn)行解析,生成“解析樹”,驗證是否關(guān)鍵字錯誤、語法錯誤、括號是否匹配,然后驗證權(quán)限

4、MySQL根據(jù)優(yōu)化器生成的執(zhí)行計劃,調(diào)用儲存引擎的API來執(zhí)行查詢

5、將結(jié)果返回給客戶端

查詢優(yōu)化器

一條查詢可以有很多種執(zhí)行方式,最后都返回相同的結(jié)果,優(yōu)化器的作用就算找到這其中最好的執(zhí)行計劃

  • 導(dǎo)致優(yōu)化器選擇錯誤的執(zhí)行計劃的原因

    • 統(tǒng)計信息不準(zhǔn)確

      MySQL依賴儲存引擎提供的統(tǒng)計信息來評估成本,但是儲存引擎提供的統(tǒng)計信息不一定準(zhǔn)確,比如InnoDB因為有MVCC的架構(gòu),并不能維護(hù)一個數(shù)據(jù)表的行數(shù)精確統(tǒng)計信息

    • 執(zhí)行計劃中的成本估算不等同與實際的執(zhí)行成本

      MySQL層面并不知道那些頁面在內(nèi)存中,哪些在磁盤上,所以查詢實際執(zhí)行過程中到底需要多少次物理IO是無法得知的

    • MySQL并不考慮其他并發(fā)執(zhí)行的查詢

    • MySQL不會考慮不受其控制的操作的成本

  • MySQL能夠處理的優(yōu)化類型

    • 重新定義關(guān)聯(lián)表的順序

    • 將外連接轉(zhuǎn)化成內(nèi)連接

    • 使用等價變換規(guī)則

      比如 5=5 AND a > 5 會改寫成 a > 5

    • 優(yōu)化COUNT()、MIN()和MAX()

      比如查找最小值,如果列在B-Tree中,那只要把最左的值拿出來就可以了;最大值把最右值拿出來就可以了

    • 預(yù)估并轉(zhuǎn)化為常數(shù)表達(dá)式

      比如表達(dá)式轉(zhuǎn)化為常數(shù),有時候甚至一個查詢也能轉(zhuǎn)化為一個常數(shù)

    • 覆蓋索引掃描

    • 子查詢優(yōu)化

    • 提前終止查詢

      發(fā)現(xiàn)已經(jīng)滿足查詢需求的時候,MySQL總是能夠立刻終止查詢。比如LIMIT

    • 等值傳播

      如果兩個列的值通過等式關(guān)聯(lián),那么MySQL能夠把其中一個列的WHERE條件傳遞到另一個上。

    • 列表IN()的比較

      在很多數(shù)據(jù)系統(tǒng)中,IN()完全等同于多個OR條件的子句,因為這兩者完全等價,但是在MySQL中不是這樣的,MySQL將IN()列表中的數(shù)據(jù)先進(jìn)行排序然后通過二分查找的方式確定列表中的值是否滿足條件,這是一個O(logn)復(fù)雜度的查詢,等級轉(zhuǎn)換成OR是O(n),所以如果IN()列表中有大量取值的時候,MySQL的處理速度回更快

MySQL如何執(zhí)行關(guān)聯(lián)查詢

按照where條件遍歷第一個表,然后循環(huán)查詢第二個表,使用on的篩選;
on的條件最好要是索引

返回結(jié)果給客戶端

一旦服務(wù)器處理完最后一個關(guān)聯(lián)表,開始生成第一條結(jié)果時,MySQL就可以開始向客戶端逐步返回結(jié)果集了;比如Java的rs.next(),一條一條取,避免服務(wù)器內(nèi)存壓力過大;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容