高性能mysql 第六章_第六章 查询性能优化
1. 為什么查詢速度會慢
如果把查詢看作是一個任務,那么它由一系列子任務組成,每個子任務都會消耗一定的時間。如果要優化,無非是減少子任務數量,或者減少子任務的執行次數。
查詢聲明周期:生成計劃,執行,返回結果給客戶端。
2. 慢查詢基礎:優化數據訪問
1.確認應用程序是否早檢索大量超過需要的數據。通常意味著訪問了太多的行,但是有時候也可能是訪問了太多的行,有時候也是可能訪問了太多的列
確認MySQL服務器是否在分析大量超過需要的數據行
2.1. 是否向數據庫請求了不需要的數據
查詢請求多余的數據,然后多余的數據會被應用程序丟棄。這會給MySQL服務器帶來額外的負擔。典型有下面四種情況:
1. 查詢了不需要的記錄
2. 多表關聯時返回全部列
3. 總是提取全部列
4. 重復查詢相同的數據
2. MySQL是否掃描了額外的記錄
響應時間
響應時間時兩個部分之和:服務時間和排隊時間。服務時間指數據庫處理這個查詢真正花了多長時間。排隊時間是指服務器等待資源而沒有真正執行查詢的時間。
掃描的行數和返回的行數
分析查詢的時候,查看掃描的行數是非常有幫助的。理想的情況下掃描的行數和返回的行數是相同的,但是這種情況并不多
掃描的行數和訪問類型
在評估查詢開銷的時候,需要考慮一下從表中找到某一行數據的成本。MySQL有好幾種方式可以查找并返回一行結果。有些訪問方式可以無須掃描就能返回查詢結果。
EXPLAIN語句中的type反應了訪問的類型。一般有全表掃描、范圍掃描、唯一索引掃描、常數引用等。
一般MySQL能使用如下三種方式來應用Where條件:
索引中使用WHERE條件來過濾不匹配的記錄。在存儲引擎完成
使用索引覆蓋掃描(Extra中出現Using index)來返回記錄,直接過濾不需要的記錄并返回命中的結果。在MySQL服務層完成的。但無需回表查詢記錄
從數據表中返回數據,然后過濾不滿足條件的記錄Extra中出現Using index)。在服務層完成,MySQL需要先從數據表讀出記錄然后過濾。
如果發現查詢需要掃描大量的數據,但只返回少量的行。可以通過如下的技巧去優化它:
使用索引覆蓋掃描,把所有的列都放到索引中,這樣存儲引擎無須回表獲取對應行就可以返回結果
改變庫表結構。如使用單獨匯總的表。
重寫復雜的查詢,讓MySQL以更有效的方式執行這個查詢。
3. 重構查詢的方式
3.1. 一個復雜的查詢還是多個簡單查詢
設計查詢的時候考慮將復雜的查詢分成多個簡單的查詢,傳統的實現中,總是強調數據庫完成盡可能多的工作。因為以前總認為網絡通信查詢解析和優化是一件代價很高的事情。這樣的想法對于MySQL并不適用,MySQL設計上讓斷開和連接都很輕量級。返回小查詢結果方面很高效。
MySQL內部每秒鐘能掃描內存中上百萬條數據,相比之下,MySQL響應給客戶端就慢很多。其他條件相同的時候,使用盡量少的查詢當然是更好的。但有時將大的查詢分成小的是有必要的。
3.2. 切分查詢
有時將大的查詢分而治之。刪除舊的數據就是很好的例子,定期刪除大的數據的,如果用一個大的數據一次性完成的話,則可能要鎖住很多數據,占滿整個事務日志,耗費資源,阻塞很多小的但重要的查詢。將一個大的查詢分成多個小的查詢可以盡可能的減小影響MySQL的性能,而且減小延遲,例如:
mysql> DELETE FROM msessages WHERE cteated < DATE_SUB(NOW(),INTERVAL 3 MONTH)
可以分解為
rows_affect = 0
do {
rows_affected = do_query(
"DELETE FROM messages WHERE created < DATE_SUB(NOW, INTERVAL 3 MONTH
LIMIT 10000")
} while rows_affected > 0
一次刪除一萬行數據,一般來說是一個比較小而且對服務器的影響也是最小的。(事務引擎,很多小事務能夠更高效)
如果每次處理后,都暫停一會再做下一次刪除,那么而可以降低服務器的影響,大大減少刪除時鎖的持有時間
3.3. 分解關聯查詢
很多高性能的應用都會對關聯查詢進行分解(將JOIN拆分成SELECT),
mysql> SELECT * FROM a JOIN b on a.id = b.id
JOIN c on b.idd = c.id WHERE a.id = 1;
改為如下查詢
mysql> SELECT * FROM a WHRE a.id = 1;
mysql> SELECT * FROM b WHERE idd = 1234;
mysql> SELECT * FROM c WHRER id in (1, 2, 3, 5)
優勢:
讓緩存效率更高。對MySQL查詢緩存來說,如果關聯中的某個表發生了變化,那么就無法查詢緩存了,拆分后,如果a.id被緩存,就會跳過第一個查詢。如果某個表很少改變,那么基于該表的查詢據可以重復利用查詢緩存結果
將查詢分解后,執行單個查詢可以減少鎖的競爭
在應用層做管理,可以更容易對數據庫進行拆分,更容易做到高性能和可擴展
查詢效率本身也會上升,在本例中,讓IN替代關聯查詢,可以讓MySQL按照ID順序進行查詢,這可能比隨機的關聯要高效
減少冗余記錄的查詢。在應用層做關聯查詢,意味著對于某條記錄只需要查詢一次,而在數據庫中做關聯查詢,則可能需要重復的訪問一部分數據,從這點看,這樣的重構還能減少網絡和內存的消耗
更進一步,相當于在應用中實現了哈希關聯,而不是用MySQL嵌套循環關聯。某些場景哈希關聯的效率要高很多
4. 查詢執行的基礎
查詢執行路徑
客戶端發送一條查詢給服務器。
服務器先檢查查詢緩存,如果命中了緩存,則立刻返回查詢中的結果
服務器進行SQL解析、預處理,再優化器生成對應的執行計劃
MySQL根據優化器生成的執行計劃,調用存儲引擎的API來執行查詢
將結果返回給客戶端
4.1. MySQL客戶端/服務器通信協議
MySQL客戶端和服務器之前的通信協議是"半雙工"的,這意味著,在任意一個時刻,要么是由客戶端向服務端發送數據,要么是服務端向客戶端發送數據。所以, 我們無需將一個消息切成小塊獨立來發送。
這種方式有明顯的限制,沒法進行流量限制。一旦一端開始發生消息,另外一端要接受完整才能響應它。
客戶端用一個單獨的數據包將查詢傳給服務器。這樣是為什么當查詢語句很長的時候,參數是max_allowed_packed就顯得特別重要。一旦客戶端發送了請求,它能做的事情就只能是等待結果了
相反,一般服務器響應給用戶的數據通常很多,由多個數據包組成。當服務器開始響應的是,客戶端必須完整地接收整個返回結果
換一種方式解釋:當客戶端從服務器取數據的時候,看起來是一個拉數據的過程。但實際是MySQL在向客戶端推送數據的過程。客戶端不斷地從接收從服務器推送的數據,客戶端無法讓服務器停下來
查詢狀態
對于一個MySQL連接,一般來說由一個狀態組成。很多種方式能查看當前態,簡單的是使用SHOW FULL PROCESSLIST命令;在一個查詢的生命周期中,狀態會變化很多次。
Sleep
線程正在等待客戶端的發送新的請求
Query
線程正在執行查詢或正在將結果發送給客戶端
Locked
在MySQL服務層,該線程正在等在鎖。在存儲引擎級別實現的鎖,如InnoDB的行鎖,并不會體現在線程狀態中。對于MyISAM來說這是一個比較的典型的狀態,但在其他沒有行鎖的引擎中也會出現
Analyzing and statistics
線程正在收集存儲引擎的信息,并生成查詢的執行計劃
Copying to tmp table [ on disk ]
線程正在執行查詢,并且將及其結果都復制到一個臨時的表,這種狀態一般要么是GROUP BY 操作,要么是文件排序操作,或者是UNION操作。如果這個狀態后面還有"on disk"標記,那表示MySQL正將一個內存臨時表放到磁盤上。
Sorting result
線程正在對結果集進行排序
Sending data
這表示多種情況:線程可能在多個狀態鍵傳送數據,或者在生成結果集,或者向客戶端返回數據
4.2. 查詢緩存
在解析一個查詢語句之前,如果查詢緩存是打開的,那么MySQL會優先檢查這個查詢是否命中查詢緩存中的數據,這個查詢是通過一個對大小寫敏感的哈希查找實現,查詢和緩存中的查詢即使有一節不同,那也不會匹配緩存結果。這種情況下查詢就會進入下一個階段的處理
4.3. 查詢優化處理
查詢的生命周期的下一步是將一個SQL轉換成一個執行計劃,MySQL再依照這個執行計劃和存儲引擎進行交互。包括:解析SQL、預處理、優化SQL執行計劃。這個過程中任何錯誤都可能終止查詢。
語法解析器和預處理
首先,MySQL通過關鍵字將SQL語句進行解析,并生成對應的解析樹。解析器使用MySQL語法規則驗證和解析查詢,例如,驗證是否使用錯誤的關鍵字,或者使用關鍵字的順序是否正確。
預處理則根據一些MySQL規則進一步檢查解析樹是否合法。例如,檢查數據表和數據列是否存在,還會解析名字和別名,看看它們是否有歧義
查詢優化器
現在語法器被認為是合法了,并且由優化器將其轉化為執行計劃。一條查詢可以由很多執行方式,最后都返回的相同的結果。優化器的作用就是找到其中最好的執行計劃
MySQL使用基于成本的優化器,它將嘗試預測一個查詢使用某種執行計劃時的成本,并選擇其中成本最小的一個。
很多中原因會導致MySQL優化器選擇錯誤的執行計劃:
統計信息不準確。MySQL依賴存儲引擎提供的統計信息來評估成本,但是有的存儲引擎提供的信息是準確的,有的偏差可能非常大。例如,InnoDB因為其MVCC的架構,并不能維護一個數據表的行數的精確統計信息
執行計劃中的成本估算不等同于實際的執行的成本。所以即使統計信息精準,優化器給出的執行計劃也可能不是最優的
MySQL的最優解基于成本模型選擇最優的計劃有時候并不是最快的執行方式。
MySQL從不考慮其他并發的執行查詢,這可能會影響到當前查詢速度
MySQL也并不是任何時候都基于成本的優化。有時會也會基于一些固定的規則。
MySQL不會考慮不受其控制的操作成本,例如執行存儲過程或這用戶自定義函數的成本
優化器還可能無法估算所有可能的執行技術,所以可能錯過實際上最優的執行計劃
MySQL查詢優化器是一個非常復雜的部件,它使用了許多優化策略來生成一個最優的執行計劃。優化策略可以分為兩種:靜態優化,動態優化。MySQL對靜態優化只會做一次,但對查詢的動態優化則在每次執行的時候都需要重新評估。有時候甚至在查詢的執行過程中也會重新優化
下面是一些MySQL能過處理的優化類型:
重新定義關聯表的順序
數據的關聯并不總是按照在查詢中指定的順序進行。決定關聯的順序是優化器很重要的一部分功能。
將外連接轉化成內連接
使用等價變換規則
優化COUNT()、MIN()和MAX()
預估并轉化為常熟數表達式
覆蓋索引g掃描
子查詢優化
提前終止查詢
等值傳播
列表 IN()的比較
4.4. 查詢執行引擎
4.5. 返回結果給客戶端
5. MySQL優化查詢器的局限性
5.1. 關聯子查詢
5.2. UNION的限制
5.3. 索引合并優化
5.4. 等值傳遞
5.5. 并行執行
5.6. 哈希關聯
5.7. 索引松散掃描
5.8. 最大值和最小值優化
6. 查詢優化器的提示
7. 優化特定類型的查詢
7.1. 優化COUNT()查詢
7.2. 優化關聯查詢
7.3. 優化子查詢
7.4. 優化GROUP BY 和 DISTINCT
7.5. 優化LIMIT分頁
7.6. 優化SQL_CALC_FOUND_ROWS
7.7. 優化UNION查詢
7.8. 靜態查詢分析
7.9. 使用用戶自定義變量
8. 案列學習
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的高性能mysql 第六章_第六章 查询性能优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是结构类型
- 下一篇: 谷歌拒付被裁员工病产假工资 导致部分员工