查詢優化是乙個非常大的話題,它可以從表設計,索引,查詢技巧等多個方面去討論,對於表設計,索引已經有專門的文章介紹(mysql高效索引策略, mysql庫表設計優化)。本文將介紹查詢優化的思路和一些實用的查詢優化策略。
1.客戶端傳送查詢請求給伺服器。
mysql客戶端/服務端通訊協議時半雙工的,任意乙個時間點,只有一方傳送資料,傳送請求後就只能等待結果;另一方接收資料,只有接受完資料才能響應結果。客戶端使用乙個單獨的資料報傳送給伺服器,當查詢語句超出max_allowed_packet,則會報錯。服務端響應資料時,則可能由多個資料報組成。
2.伺服器檢查查詢快取,若命中則返回快取資料。否則進行下一步
伺服器收到查詢請求後,先經過查詢快取,如果命中,則返回結果。否則進行sql的解析以及後續流程。
3.服務端進行sql解析和預處理
mysql解析器將使用語法規則驗證和解析查詢,得到乙個解析樹。預處理器會檢查解析樹中的列,別名。
4.優化器生成查詢計畫,並呼叫儲存引擎api執行查詢。
mysql使用基於成本的優化器,它**乙個查詢使用某種執行計畫的成本,並選擇成本最小的乙個。執行計畫的依據來自統計資訊,統計資訊由儲存引擎api提供。優化器會根據查詢上下文進行優化,比如重新定義關聯表順序,覆蓋索引掃瞄,子查詢優化等。
5.返回結果給客戶端。
如果查詢可以被快取,那麼mysql會將結果放到查詢快取中。mysql結果集的返回是乙個增量逐步返回的過程。在生成第一條結果集時就開始逐步向客戶端返回。每一行都會以乙個滿足mysql客戶端/服務端通訊協議的包傳送出去。在tcp協議傳輸層可能會對mysql的包進行快取然後批量傳輸。
整體流程如下圖所示(引自《高效能mysql》)
mysql是巢狀迴圈來執行關聯操作的,即先在一張表中迴圈取單條資料,再巢狀關聯表迴圈取出匹配的行,依次下去直到所有表都掃瞄完成。然後根據各個表匹配的行返回查詢字段。其實就是k張表分別放在巢狀迴圈的不同層,第一張表在第一層迴圈裡遍歷,第二張表在第二層迴圈裡,…,第k張表在第k層迴圈裡。如果沒有索引的話就是乙個o(n ^ k)的複雜的演算法。(引自《高效能mysql》)
關聯查詢可能會被優化器重寫關聯順序,選擇更小的表為驅動表,以提高查詢效率。
--檢視查詢執行計畫
explain
select..
.--檢視不被優化器重寫關聯順序的執行計畫
explain
select straight_join ...
--檢視最後一次執行的成本,該數值越小越優
show
status
like
`last_query_cost`
;
mysql在from子句中遇到子查詢時,先執行子查詢並將結果放到一張臨時表中,然後將這個臨時表當做乙個普通表對待。mysql在執行union查詢時也使用類似的臨時表。臨時表是沒有索引的,所以在執行這類查詢時需要注意複雜度不要太高。
在mysql索引優化中有介紹利用索引排序,當不能使用索引排序時,mysql需要在服務層排序。如果資料量小則在記憶體中進行快速排序,如果資料量大則進行歸併排序,統稱為檔案排序。有兩種排序策略
兩次傳輸排序
先讀取行指標及排序字段,在記憶體中完成排序後再回表查詢資料。如果記錄較多會產生大量隨機io,但是這種方式單次能進行更多記錄的排序。
單次傳輸排序
將資料一次性讀入記憶體,再排序。這種方式更加佔記憶體空間。當查詢需要所有列的總長度不超過 max_length_for_sort_data時,使用該排序策略。
關聯查詢排序
如果排序字段來自關聯查詢的第一張表,則在查詢第乙個表的時候就排序,explain的extra欄位會有「using filesort」;否則需要將關聯的結果存放到一張臨時表,所有的關聯結束後再進行檔案排序,explain結果的extra欄位為「using temporary, using filesort」。
盡量請求所需的資料就好了。當然在設計介面時,有時候為了提高介面的通用性,會在sql種包含很多查詢條件,很多返回字段。需要在兩者之間權衡,對於大多數普通的應用來說,可能選擇通用的設計,減少開發工作量。
如果儲存引擎層面沒能過濾掉資料,那麼只能到服務層做過濾。而關於where條件的應用是:
第一,在索引中應用where條件來過濾記錄。
第二,使用索引覆蓋,不需要回表。
第三,在服務層應用where條件。
這三種方式從好到壞。當發現掃瞄過多的行後,新增合適的索引可以避免過多的記錄需要返回到服務層過濾(導致大量隨機io)。
現在一般的網路代價較低,相比而言,將乙個複雜的查詢切分成幾個小的簡單查詢更有利於mysql執行。
將乙個大事務切分成小事務,比如在定期清理歷史資料時,如果用乙個大的sql可能會一次鎖定很多資料,可以將歷史資料分批刪除,並且每執行乙個sql間隔一段時間,平緩的傳送請求,這樣可以降低mysql的壓力。
將關聯查詢分解為簡單查詢,mysql能很好的執行小查詢。單個查詢能夠減少鎖的競爭,並且可以更好的利用快取。將關聯的邏輯放到伺服器做就可以了。
count可以統計某個列(非空)的數量,如果單單統計行數,則應當使用count(*)。對於count可以有以下優化。
a.如果不需要精確值,使用explain估算出來的行數做近似值,explain沒有真正執行查詢,消耗很低。
b.如果不需要精確值,可以定期執行count,將結果快取到redis中。
c.如果需要精確值,在有效能壓力的時候,使用計數器表單獨計數。
a.在關聯表中給關聯欄位加索引,例如優化器的結果是a join b ,則給b的關聯欄位加索引,而a不需要。
b.確保group by 和 order by中的表示式只涉及乙個表中的列。
在索引優化一文中有將過利用索引進行分組和排序。如果不能用到索引,那麼只能使用臨時表或者檔案排序。
物理分頁是通過limit實現的,當頁碼很大時,例如 limit 10000 10,將查詢滿足條件的10010條記錄,然後排序,返回最後10行,這樣的代價非常大。可以嘗試如下方式進行優化。
a.索引覆蓋,即使深度分頁,也不會有大量的隨機io。但是大多數分頁是很難滿足這個條件的。查詢的字段很多,條件很雜,難以走索引覆蓋。
b.延遲關聯,先用過濾條件和分頁條件查詢出id,這個查詢過程需要保證有合適的索引。由於只查詢id不用回表,不會有大量隨機io。然後,用這批id去關聯查詢需要的字段。注意需要where條件走索引覆蓋。
select
*from t_table t1
inner
join
(select id from t_table where..
.limit
1000
10) t2
on t1.id = t2.id;
c.利用order by的最值分頁。比如order by id asc,先查詢出第一頁資料,之後每次查詢都帶上上一次查詢結果的最大id。
select
*from t_table where id >
1000
and.
.order
by field asc
limit
10
d.冗餘表,該錶只包含主鍵和需要查詢和排序的資料列。先到冗餘表中查詢id,再根據id去原表查資料。這種方式和延遲關聯思想類似。
a.所有union的條件,where,limit,order by等字句下沉到各個子查詢中。
b.盡量使用union all,然後在應用層做去重。
查詢優化(MySQL優化查詢)
關聯查詢太多join 設計缺陷或不得已的需求 資料庫伺服器調優及各個引數設定不適當 緩衝 執行緒數等 慢查詢日誌 找出執行速度慢的sql語句 慢查詢的開啟並捕獲 explain 慢sql分析 show profile查詢sql在mysql伺服器裡面的執行細節和生命週期情況 sql資料庫伺服器的引數調...
mysql統計查詢優化 Mysql查詢優化
效能涉及的層面很多,但是在操作層面,主要有表結構設計優化 索引優化和查詢優化 查詢的生命週期大致可以分為,從客戶端 到服務端 在伺服器上解析 生成執行計畫 執行 返回結果給客戶端 sql執行流程 具體優化技巧 1.消除外連線 2.消除子查詢 盡量用join代替子查詢,雖說mysql查詢優化器會進行優...
MySQL優化 查詢優化
在每乙個消耗大量時間的查詢中,都能看到一些不必要的額外操作 某些操作被額外地重複了很多次 某些操作執行得太慢等。優化查詢的目的就是減少和消除這些操作所花費的時間。查詢效能低下最基本的原因是訪問的資料太多。所以需要考慮是否向資料庫請求了不需要的資料 1 多表關聯時,或獲取單錶資料時,盡量避免不加思考地...