Mysql 查詢性能優化

注:本篇是《高性能Mysql》第三版的讀書筆記

 

查詢性能優化

查詢性能低下的基本原因就是訪問的數據量太多,而有些數據根本就用不着,或者mysql在進行分析時存在大量超過結果行的數據

一些經典案例對性能造成損失

1、查詢不需要的記錄,返回沒有limit

2、多表關聯時返回全部列。

3、總是取出全部列。這個需要作出取捨。

4、重複查詢相同的數據,這種最好將這種數據進行緩存。

對於mysql最簡單衡量查詢開銷的三個指標如下

  • 響應時間
  • 掃描的行數
  • 返回的行數

響應時間是兩個的和:服務時間和排隊時間。排隊時間指的是獲取鎖或者IO等到的時間etc.

掃描行數和返回行數這兩個就像是hash&鏈表取數據一樣  肯定希望像hash這樣  o1的複雜度不需要掃描額外的數據行。

explain 中的type反應了訪問類型,訪問類型有很多,包括從全表掃描all到範圍到索引等等。所代表的訪問行數就完全不一樣了。

rows代表預估掃描的行數

extra 表示mysql將通過哪種方式篩選存儲引擎返回的記錄。   using where 表示使用where條件。

mysql可以以三種方式應用where條件,下面爲分別好到壞

  1. 在索引中使用where條件來過濾不匹配的記錄,這是在存儲引擎層完成的。
  2. 使用索引覆蓋掃描,(using index)來返回記錄,直接從索引中過濾了不需要的數據並命中返回的結果,這是在mysql服務器層完成,但無需再回表查詢記錄。
  3. 從數據表中返回數據,然後過濾不滿足條件的記錄(using where)這是在mysql服務器層完成的,mysql需要先從數據表讀取記錄然後過濾。

 

select a_id,count(*) from tab group by a_id; 這中分組會掃描大量的數據行並返回少量數據,有很大優化空間

  • 使用索引覆蓋掃描,把所有需要用到的列放到索引中,這樣存儲引擎就無須回表獲取對應行就可以返回結果了。
  • 改變表結構,使用匯總表。這個對於不需要實時展示的數據(接受延遲)很好用。響應時間性能會提高很多倍。
  • 重寫這個查詢。

重構查詢的方式

  1. 可以將一個複雜查詢拆分成多個小查詢(如果合理的話)
  2. 切分查詢,例如刪除舊數據,可以寫一個定時任務,每次刪點,大大減少了一次刪除大量數據對服務器造成的影響。
  3. 分解關聯查詢
select * from tag 

left join tag_post on tag_post.tag_id = tag.id 

left join post on tag_post.id = post.id  

where tag.tag = 'mysql'

可以拆解成

select * from tag where tag = 'mysql'

select * from tag_post where tag_id = 123

select * from post where post.id in (1,2,3,4,5)
  • 分解關聯查詢會讓緩存的效率更高,如果變化較小就可以很好的服用mysql的查詢緩存。用到緩存之後一些步驟就秒執行成了
  • 將查詢分解後,執行單個查詢可以減少鎖的競爭。
  • 在應用層做關聯,可以更容易對數據庫進行拆分,提高性能和擴展性。
  • 查詢本身的效率可能也會提升,in 會按照順序進行查詢,比隨機關聯更高效。
  • 可以減少冗餘的記錄。
  • 相當於在應用層做的哈希關聯,效率要比在mysql的嵌套循環高。

 

一個查詢的過程如下

mysql客戶端和服務端之間的通訊協議是半雙工的,任意時刻,客戶端與服務端只能單向發送數據。

一端開始發送消息,另一端接收完整消息纔可以響應。

當使用mysql的庫函數取數據時,看起來像是從mysql服務器取數據,但是實際上是從庫函數緩存取數據。將數據緩存到內存來提高性能。這個查詢緩存在不同的編程語言調用mysql的時候都可以設置和指定要不要使用緩存。

對於取大量結果集的操作不適合用查詢緩存,庫函數會花很多時間和內存存儲所有的結果集。

 

查詢狀態

對於一個mysql連接或者說一個線程,任何時刻都有一個狀態。該狀態表示mysql當前正在做什麼。command表示狀態。

查詢緩存

在解析一個查詢語句之前,如果查詢緩存是打開的,mysql會優先檢查這個查詢是否命中查詢緩存中的數據,這個檢查是通過一個對大小寫敏感的哈希查找實現的。

 

查詢優化處理

包括:解析sql,預處理,優化sql執行計劃。

mysql通過關鍵字將sql語句進行解析並生成一顆相應的解析樹。

下面是一些mysql能夠處理的優化類型:

  1. 重新定義關聯表的順序
  2. 將外連接轉化成內連接
  3. 使用等價變換規則
  4. 優化count() min() max()
  5. 預估並轉化爲常數表達式
  6. 覆蓋索引掃描
  7. 子查詢優化
  8. 提前終止查詢
  9. 等值傳播
  10. 列表in()的比較  in會先進行排序。

 

mysql如何執行關聯查詢

mysql執行關聯查詢的策略很簡單:mysql對任何關聯都執行嵌套循環關聯操作,mysql先在一個表中循環取出單條的數據,然後在嵌套循環到下一個表中尋找匹配的行,直到所有表中的行匹配爲止。可想而知,被連接表的列有索引是多麼關鍵的!

 

mysql優化器最重要的一部分就是關聯查詢優化器,它決定了多個表關聯時候的順序,不同的順序意味着不同的掃描行數。優化器會將sql的關聯表順序進行優化使其有更少的掃描行數。

 

排序優化

mysql在排序時是可以使用索引的,不過要遵循索引的使用規則,例如聯合索引,要保證排序字段出現順序與聯合索引保持最左匹配法則。

mysql在排序的時候可以使用index或者file sort  儘量保證使用index 性能好, filesort會大量使用內存數據量大則更會使用磁盤。

extra 中包括 using filesort 表示使用了file sort進行排序,  using temporary 表示使用了臨時表。

5.6版本以後的mysql 在limit和order by 的時候不再對所有結果進行排序,刨除不滿足的結果再進行排序,可想而知效率提高了。

 

索引合併優化

注⚠️:當where條件字句中包含多個複雜條件的時候,mysql能夠訪問單個表的多個索引以合併和交叉過濾的方式來定位行數據。

 

優化特定類型的查詢

count函數多用來計算列的非null數量或者行數count(*)

對於myisam對count(*)有優化,當然只侷限於沒有進行where條件查詢時,或者對於非null的列。其他列就沒有優化了。

 

優化關聯查詢

  1. 確保on或者using子句中的列有索引
  2. 確保任何group by order by 中的表達式儘量只有一個列,這樣更好使用索引進行優化
  3. 升級mysql時,關聯語法、運算符優先級可能會變化

 

優化group by&&distinct

當無法使用索引時,group by 有兩種優化策略,分別爲使用臨時表或者文件排序來分組。

注:如果沒有通過order by 顯示指定了排序的列,當查詢使用group by 的時候,結果集會按照分組的字段進行排序。這種排序又導致了需要文件排序。也可以group by 時指定 asc  desc

優化limit

可以使用上次查詢的id  來做where篩選進而達到優化limit的效果。

 

優化union

union  是去重的,取數據的時候用了臨時表,把數據扔到臨時表裏,再從臨時表取數據返回給客戶端,
union會對臨時表中所有數據進行distinct操作(實際上distinct會對select的所有列去重,因此union是所有列去重)
distinct是遍歷比較進行排序去重的,因此數據量大的時候很恐怖,有很大的性能問題。

 

 

 

 

 

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