本文內容基於《高效能mysql》第三版,寧海元、周振興、彭立勛、翟衛祥等譯。
高效能:庫表結構優化、索引優化、查詢優化。
如果要優化查詢,實際上要優化其子任務,要麼消除其中一部分子任務,要麼減少子任務的執行次數,要麼讓子任務執行得更快。
通常來說,查詢的生命週期大致可以按照順序來看:從客戶端,到伺服器,然後在伺服器上進行解析,生成執行計畫,執行,並返回結果給客戶端。
在完成這些任務的時候,查詢需要在不同的地方花費時間,包括網路、cpu計算、生成統計資訊和執行計畫、鎖等待等操作,尤其是向底層儲存引擎檢索資料的呼叫操作,這些呼叫需要在記憶體操作、cpu操作和記憶體不足時導致的i/o操作上消耗時間。根據儲存引擎不同,可能還會產生大量的上下文切換以及系統呼叫。
查詢效能低下最基本的原因是訪問的資料太多。對於低效的查詢,通過下面兩個步驟來分析總是很有效的:
2.1.1 查詢不需要的記錄
乙個常見的錯誤是常常會誤以為mysql會只返回需要的資料,實際上mysql卻是先返回全部結果集再進行計算。最簡單有效的解決方法是在這樣的查詢後面加上limit。
2.1.2 多表關聯時返回全部列
正確的方式應該是只取需要的列。
2.1.3 總是取出全部列
select *會讓優化器無法完成索引覆蓋掃瞄這類優化,還會為伺服器帶來額外的i/o、記憶體和cpu的消耗。
2.1.4 重複查詢相同的資料
當初次查詢的時候將這個資料快取起來,需要的時候從快取中取出。
在確定查詢只返回需要的資料以後,接下來應該看看查詢為了返回結果是否掃瞄了過多的資料。對於mysql,最簡單的衡量查詢開銷的三個指標是:
這三個指標都會記錄到mysql的慢日誌中,所以檢查慢日誌記錄是找出掃瞄行數過多的查詢的好辦法。
2.2.1 響應時間
響應時間是兩個部分之和:服務時間和排隊時間。
服務時間是指資料庫處理這個查詢真正花了多長時間。
排隊時間是指伺服器因為等待某些資源而沒有真正執行查詢的時間,可能是等i/o操作完成,也可能是等待行鎖等。
當看到乙個查詢的響應時間的時候,確定這個時間是否是乙個合理的值。了解這個查詢需要哪些索引以及它的執行計畫是什麼,然後計算大概需要多少個順序和隨機i/o,再用其乘以在具體硬體條件下一次i/o的消耗時間,最後把這些消耗都加起來,就可以獲得乙個大概參考值來判斷當前響應時間是不是乙個合理的值。
2.2.2 掃瞄的行數和返回的行數
理想情況下掃瞄的行數和返回的行數應該是相同的,但是關聯查詢中,伺服器必須要掃瞄多行才能生成結果集中的一行。
2.2.3 掃瞄的行數和訪問型別
在explain語句的type列反應了訪問型別,速度從慢到快,掃瞄行數從多到少依次是:全表掃瞄、索引掃瞄、範圍掃瞄、唯一索引掃瞄、常數引用等。
一般mysql能夠使用如下三種方式應用where條件,從好到壞依次為:
如果發現查詢需要掃瞄大量的資料但只返回少數的行,那麼可以這樣去優化:
在優化有問題的查詢時,目標應該是找到乙個更優的方法獲得實際需要的結果,而不一定總是需要從mysql獲取一模一樣的結果集。
mysql從設計上讓連線和斷開連線都很輕量級,在返回乙個小的查詢結果方面很高效。mysql內部每秒能夠掃瞄記憶體中上百萬行資料,相比之下,mysql響應資料給客戶端就慢得多了。
有時候對於乙個大查詢需要「分而治之」,將打查詢切分成小查詢,每個查詢功能完全一樣,只完成一小部分,每次只返回一小部分查詢結果。
例如以下查詢:
delete from message where create_time < date_sub(now(),interval 3 month);
可以用類似的方法完成同樣的工作:
delete from message where create_time < date_sub(now(),interval 3 month) limit 10000;
一次刪除一萬行資料一般來說是乙個比較高效而且對伺服器影響也最小的做法,並且,如果每次刪除資料後,都暫停一會兒再做下一次刪除,這樣也可以將伺服器上原本一次性的壓力分散到乙個很長的時間段中,就可以大大降低對伺服器的迎新愛過,還可以大大減少刪除時鎖的持有時間。
用分解關聯查詢的方式重構查詢有如下的優勢:
在很多場景下,通過重構查詢將關聯放到應用程式中將會更加高效,這樣的場景有很多,比如:當應用能夠方便地快取單個查詢的結果的時候,當可以將資料分布到不同的mysql伺服器上的時候,當能過使用in()的方式代替關聯查詢的時候,當查詢中使用同乙個資料表的時候。
count()的作用:
在同乙個查詢中統計同乙個列的不同值的數量:
select
sum(if(name = 'a', 1, 0)) as a,
sum(if(name = 'b', 1, 0)) as b
from
film;
select
sum(name = 'a') as a,
sum(name = 'b') as b
from
film;
select
count(name = 'a' or null) as a,
count(name = 'b' or null) as b
from
film;
在低於5.6的版本中,盡可能使用關聯查詢代替子查詢。
最有效的優化辦法是使用索引來優化。當無法使用索引的時候,group by使用兩種策略來完成:使用臨時表或者檔案排序來做分組。
如果需要對關聯查詢做分組,並且是按照查詢表中的某個列進行分組,那麼通常採用查詢表的標誌列分組的效率會比其他列更高。
在系統中需要進行分頁操作的時候,通常會使用limit加上偏移量的辦法實現,同時加上合適的order by子句。如果有對應的索引,通常效率會不錯,否則,mysql需要做大量的檔案排序操作。
在偏移量非常大的時候,代價非常高,優化方法是盡可能使用所以覆蓋掃瞄,而不是查詢所有的列。
參考高效能mysql之建立高效能的索引(三)中的「延遲關聯」方法。
mysql總是通過建立並填充臨時表的方式來執行union查詢,並且經常需要收工地將where、limit、order by等子句「下推」到union的各個子查詢中,以便優化器可以充分利用這些條件進行優化。
除非確實需要伺服器消除重複的行,否則就一定要使用union all,這一點很重要。如果沒有all關鍵字,mysql會給臨時表加上distinct選項,這會導致對整個臨時表的資料做唯一性檢查,這樣做的代價非常高。
《高效能MySQL》之MySQL查詢效能優化
響應時間過長。如果把查詢看做是乙個任務,那麼它由一系列子任務組成,每個子任務都會消耗一定的時間。如果要優化查詢,實際上優化其子任務,要麼消除其中一些子任務,要麼減少子任務的執行次數,要麼讓子任務執行得更快。查詢的生命週期 客戶端 伺服器 伺服器上解析 生成執行計畫 執行 返回結果給客戶端。其中 執行...
高效能MySQL之查詢優化
查詢執行引擎 結果返回給客戶端 mysql查詢的一些優化建議 reference 查詢優化 索引優化 庫表結構優化需要齊頭並進,乙個不落。檢視慢查詢時間 show variables like long query time 預設10s 檢視慢查詢配置情況 show status like slow...
《高效能MySQL》第6章 查詢效能優化
6.2 慢查詢基礎 優化資料訪問 6.2.1 是否向資料庫請求了不需要的資料 有些查詢會請求超過實際需要的資料,然後這些多餘的資料會被應用程式丟棄。這會給mysql伺服器帶來額外的負擔,並增加網路開銷,另外也會消耗應用伺服器的cpu和記憶體資源。6.2.2 mysql是否在掃瞄額外的記錄6.3 重構...