轉載:MYSQL優化(一):MySQL 查詢過程、查詢緩存及 SQL_CACHE與SQL_NO_CACHE的用法

MySQL查詢過程

我們總是希望MySQL能夠獲得更高的查詢性能,最好的辦法是弄清楚MySQL是如何優化和執行查詢的。一旦理解了這一點,就會發現:很多的查詢優化工作實際上就是遵循一些原則讓MySQL的優化器能夠按照預想的合理方式運行而已。

當向MySQL發送一個請求的時候,MySQL到底做了些什麼呢?

MySQL查詢過程

客戶端/服務端通信協議

MySQL客戶端/服務端通信協議是“半雙工”的:在任一時刻,要麼是服務器向客戶端發送數據,要麼是客戶端向服務器發送數據,這兩個動作不能同時發生。一旦一端開始發送消息,另一端要接收完整個消息才能響應它,所以我們無法也無須將一個消息切成小塊獨立發送,也沒有辦法進行流量控制。

客戶端用一個單獨的數據包將查詢請求發送給服務器,所以當查詢語句很長的時候,需要設置max_allowed_packet參數。但是需要注意的是,如果查詢實在是太大,服務端會拒絕接收更多數據並拋出異常。

與之相反的是,服務器響應給用戶的數據通常會很多,由多個數據包組成。但是當服務器響應客戶端請求時,客戶端必須完整的接收整個返回結果,而不能簡單的只取前面幾條結果,然後讓服務器停止發送。因而在實際開發中,儘量保持查詢簡單且只返回必需的數據,減小通信間數據包的大小和數量是一個非常好的習慣,這也是查詢中儘量避免使用SELECT *以及加上LIMIT限制的原因之一。

MySQL查詢緩存

在解析一個查詢語句前,如果查詢緩存是打開的,那麼MySQL會檢查這個查詢語句是否命中查詢緩存中的數據。如果當前查詢恰好命中查詢緩存,在檢查一次用戶權限後直接返回緩存中的結果。這種情況下,查詢不會被解析,也不會生成執行計劃,更不會執行。

MySQL將緩存存放在一個引用表(不要理解成table,可以認爲是類似於HashMap的數據結構),通過一個哈希值索引,這個哈希值通過查詢本身、當前要查詢的數據庫、客戶端協議版本號等一些可能影響結果的信息計算得來。所以兩個查詢在任何字符上的不同(例如:空格、註釋),都會導致緩存不會命中。

如果查詢中包含任何用戶自定義函數、存儲函數、用戶變量、臨時表、mysql庫中的系統表,其查詢結果
都不會被緩存。比如函數NOW()或者CURRENT_DATE()會因爲不同的查詢時間,返回不同的查詢結果,再比如包含CURRENT_USER或者CONNECION_ID()的查詢語句會因爲不同的用戶而返回不同的結果,將這樣的查詢結果緩存起來沒有任何的意義。

既然是緩存,就會失效,那查詢緩存何時失效呢?MySQL的查詢緩存系統會跟蹤查詢中涉及的每個表,如果這些表(數據或結構)發生變化,那麼和這張表相關的所有緩存數據都將失效。正因爲如此,在任何的寫操作時,MySQL必須將對應表的所有緩存都設置爲失效。如果查詢緩存非常大或者碎片很多,這個操作就可能帶來很大的系統消耗,甚至導致系統僵死一會兒。而且查詢緩存對系統的額外消耗也不僅僅在寫操作,讀操作也不例外:

  1. 任何的查詢語句在開始之前都必須經過檢查,即使這條SQL語句永遠不會命中緩存

  2. 如果查詢結果可以被緩存,那麼執行完成後,會將結果存入緩存,也會帶來額外的系統消耗

基於此,我們要知道並不是什麼情況下查詢緩存都會提高系統性能,緩存和失效都會帶來額外消耗,只有當緩存帶來的資源節約大於其本身消耗的資源時,纔會給系統帶來性能提升。但要如何評估打開緩存是否能夠帶來性能提升是一件非常困難的事情,也不在本文討論的範疇內。如果系統確實存在一些性能問題,可以嘗試打開查詢緩存,並在數據庫設計上做一些優化,比如:

  1. 用多個小表代替一個大表,注意不要過度設計

  2. 批量插入代替循環單條插入

  3. 合理控制緩存空間大小,一般來說其大小設置爲幾十兆比較合適

  4. 可以通過SQL_CACHESQL_NO_CACHE來控制某個查詢語句是否需要進行緩存

最後的忠告是不要輕易打開查詢緩存,特別是寫密集型應用。如果你實在是忍不住,可以將query_cache_type設置爲DEMAND,這時只有加入SQL_CACHE的查詢纔會走緩存(理由查看下面的介紹),其他查詢則不會,這樣可以非常自由地控制哪些查詢需要被緩存。

當然查詢緩存系統本身是非常複雜的,這裏討論的也只是很小的一部分,其他更深入的話題,比如:緩存是如何使用內存的?如何控制內存的碎片化?事務對查詢緩存有何影響等等,讀者可以自行閱讀相關資料,這裏權當拋磚引玉吧。

關於SQL_CACHE與SQL_NO_CACHE

MySql中可以在SQL中指定SQL_CACHESQL_NO_CACHE來控制某個查詢語句是否需要進行緩存

關於 query_cache_type變量

mysql是根據query_cache_type這個變量來決定要不要把查詢結果放到查詢緩存中。
這個變量有三個取值:0,1,2,分別代表了off、on、demand。

mysql默認爲開啓 on

例如在my.ini中增加一行 :query_cache_type=2

當query_cache_type=0,query cache 是關閉的。

當query_cache_type=1,那麼查詢總是先到查詢緩存中查找,即使使用了sql_no_cache仍然查詢緩存,因爲sql_no_cache只是不緩存查詢結果,而不是不使用查詢結果。

當query_cache_type=2,demand。

則只有加入SQL_CACHE的查詢纔會走緩存

(1)當爲select語句時:

flushCache默認爲false,表示任何時候語句被調用,都不會去清空本地緩存和二級緩存。

useCache默認爲true,表示會將本條語句的結果進行二級緩存。

(2)當爲insert、update、delete語句時:

flushCache默認爲true,表示任何時候語句被調用,都會導致本地緩存和二級緩存被清空。這就會有前面的mysql數據庫僵死,因爲這三種語句需要將緩存清空。

useCache屬性在該情況下沒有。

當爲select語句的時候,如果沒有去配置flushCache、useCache,那麼默認是啓用緩存的,所以,如果有必要,那麼就需要人工修改配置,修改結果類似下面:

<select id="save" parameterType="XX" flushCache="true" useCache="false">
    ……
</select>
update 的時候如果 flushCache="false",則當你更新後,查詢的數據數據還是老的數據。


注:修改變量配置,需要重啓mysql服務

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