【MySQL】Query Cache 優化

注:本文內容來自MySQL大神:簡朝陽http://isky000.com/ 的MySQL性能調優與架構設計》一書 mysql

談到Query Cache,恐怕使用過MySQL 的大部分人都會或多或少有一些瞭解,由於在不少人看來他能夠幫助咱們將數據庫的性能產生一個「質」的提高。但真的是這樣嗎?這一節咱們就將如何合理的使用MySQL 的Query Cache 進行一些相應的分析並得出部分優化建議。 算法

Query Cache 真的是「尚方寶劍」嗎? sql

Query Cache實現原理


MySQL 的Query Cache 實現原理實際上並非特別的複雜,簡單的來講就是將客戶端請求的Query語句(固然僅限於SELECT 類型的Query)經過必定的hash 算法進行一個計算而獲得一個hash 值,存放在一個hash 桶中。同時將該Query 的結果集(Result Set)也存放在一個內存Cache 中的。存放Queryhash 值的鏈表中的每個hash 值所在的節點中同時還存放了該Query 所對應的Result Set 的Cache 所在的內存地址,以及該Query 所涉及到的全部Table 的標識等其餘一些相關信息。系統接受到任何一個SELECT 類型的Query 的時候,首先計算出其hash 值,而後經過該hash 值到Query Cache 中去匹配,若是找到了徹底相同的Query,則直接將以前所Cache 的Result Set 返回給客戶端而徹底不須要進行後面的任何步驟便可完成此次請求。然後端的任何一個表的任何一條數據發生變化以後,也會通知QueryCache,須要將全部與該Table 有關的Query 的Cache 所有失效,並釋放出以前佔用的內存地址,以便後面其餘的Query 可以使用。 數據庫

從上面的實現原理來看,Query Cache 確實是以比較簡單的實現帶來巨大性能收益的功能。可是不少人可能都忽略了使用QueryCache 以後所帶來的負面影響: 後端

a) Query 語句的hash 運算以及hash 查找資源消耗。當咱們使用Query Cache 以後,每條SELECT類型的Query 在到達MySQL 以後,都須要進行一個hash 運算而後查找是否存在該Query 的Cache,雖然這個hash 運算的算法可能已經很是高效了,hash 查找的過程也已經足夠的優化了,對於一條Query 來講消耗的資源確實是很是很是的少,可是當咱們每秒都有上千甚至幾千條Query 的時候,咱們就不能對產生的CPU 的消耗徹底忽視了。 緩存

b) Query Cache 的失效問題。若是咱們的表變動比較頻繁,則會形成Query Cache 的失效率很是高。這裏的表變動不只僅指表中數據的變動,還包括結構或者索引等的任何變動。也就是說咱們每次緩存到Query Cache 中的Cache 數據可能在剛存入後很快就會由於表中的數據被改變而被清除,而後新的相同Query 進來以後沒法使用到以前的Cache。 架構

c) Query Cache 中緩存的是Result Set ,而不是數據頁,也就是說,存在同一條記錄被Cache 屢次的可能性存在。從而形成內存資源的過渡消耗。固然,可能有人會說咱們能夠限定Query 函數

Cache 的大小啊。是的,咱們確實能夠限定Query Cache 的大小,可是這樣,Query Cache 就很容易形成由於內存不足而被換出,形成命中率的降低。 性能

對於Query Cache 的上面三個負面影響,若是單獨拿出每個影響來講都不會形成對整個系統多大的問題,並不會讓你們對使用Query Cache 產生太多顧慮。可是,當綜合這三個負面影響一塊兒考慮的話,恐怕Query Cache 在不少人心目中就再也不是之前的那把「尚方寶劍」了。 優化

適度使用Query Cache

雖然Query Cache 的使用會存在一些負面影響,可是咱們也應該相信其存在是一定有必定價值。咱們徹底不用由於Query Cache 的上面三個負面影響就徹底失去對Query Cache 的信心。只要咱們理解了Query Cache 的實現原理,那麼咱們就徹底能夠經過必定的手段在使用Query Cache 的時候揚長避短,重發發揮其優點,並有效的避開其劣勢。

首先,咱們須要根據Query Cache 失效機制來判斷哪些表適合使用Query 哪些表不適合。因爲QueryCache 的失效主要是由於Query 所依賴的Table 的數據發生了變化,形成Query 的Result Set 可能已經有所改變而形成相關的Query Cache 所有失效,那麼咱們就應該避免在查詢變化頻繁的Table 的Query 上使用,而應該在那些查詢變化頻率較小的Table 的Query 上面使用。MySQL 中針對Query Cache 有兩個專用的SQL Hint(提示):SQL_NO_CACHE 和SQL_CACHE,分別表明強制不使用Query Cache 和強制使用Query Cache。咱們徹底能夠利用這兩個SQL Hint,讓MySQL 知道咱們但願哪些SQL 使用Query Cache 而哪些SQL 就不要使用了。這樣不只可讓變化頻繁Table 的Query 浪費Query Cache 的內存,同時還能夠

減小Query Cache 的檢測量。

其次,對於那些變化很是小,大部分時候都是靜態的數據,咱們能夠添加SQL_CACHE 的SQL Hint,強制MySQL 使用Query Cache,從而提升該表的查詢性能。

最後,有些SQL 的Result Set 很大,若是使用Query Cache 很容易形成Cache 內存的不足,或者將以前一些老的Cache 沖刷出去。對於這一類Query 咱們有兩種方法能夠解決,一是使用SQL_NO_CACHE 參數來強制他不使用Query Cache 而每次都直接從實際數據中去查找, 另外一種方法是經過設定「query_cache_limit」參數值來控制Query Cache 中所Cache 的最大Result Set ,系統默認爲1M(1048576)。當某個Query 的Result Set 大於「query_cache_limit」所設定的值的時候,QueryCache 是不會Cache 這個Query 的。

Query Cache 的相關係統參數變量和狀態變量

咱們首先看看Query Cache 的系統變量,能夠經過執行以下命令得到MySQL 中Query Cache 相關的系統參數變量:

mysql> show variables like '%query_cache%';

+------------------------------+-----------+

| Variable_name | Value |

+------------------------------+-----------+

| have_query_cache | YES |

| query_cache_limit | 1048576 |

| query_cache_min_res_unit | 4096 |

| query_cache_size | 268435456 |

| query_cache_type | ON |

| query_cache_wlock_invalidate | OFF |

+------------------------------+-----------+

● 「have_query_cache」:該MySQL 是否支持Query Cache;

● 「query_cache_limit」:Query Cache 存放的單條Query 最大Result Set ,默認1M;

● 「query_cache_min_res_unit」:Query Cache 每一個Result Set 存放的最小內存大小,默認4k;

● 「query_cache_size」:系統中用於Query Cache 內存的大小;

● 「query_cache_type」:系統是否打開了Query Cache 功能;

● 「query_cache_wlock_invalidate」:針對於MyISAM 存儲引擎,設置當有WRITE LOCK 在某個Table 上面的時候,讀請求是要等待WRITE LOCK 釋放資源以後再查詢仍是容許直接從QueryCache 中讀取結果,默認爲FALSE(能夠直接從Query Cache 中取得結果)。

以上參數的設置主要是「query_cache_limit」和「query_cache_min_res_unit」兩個參數的設置須要作一些針對於應用的相關調整。若是咱們須要Cache 的Result Set 通常都很小(小於4k)的話,能夠適當將「 query_cache_min_res_unit 」 參數再調小一些, 避免形成內存的浪費,「query_cache_limit」參數則不用調整。而若是咱們須要Cache 的Result Set 大部分都大於4k 的話,則最好將「query_cache_min_res_unit」調整到和Result Set 大小差很少,「query_cache_limit」的參數也應大於Result Set 的大小。固然,可能有些時候咱們比較難準確的估算Result Set 的大小,那麼當Result Set 較大的時候,咱們也並非非得將「query_cache_min_res_unit」設置的和每一個Result Set 差很少大,是每一個結果集的一半或者四分之一大小均可以,要想很是完美的徹底不浪費任何內存確實也是不可能作到的。

若是咱們要了解Query Cache 的使用狀況,則能夠經過Query Cache 相關的狀態變量來獲取,如經過以下命令:mysql> show status like 'Qcache%';

+-------------------------+------------+

| Variable_name | Value |

+-------------------------+------------+

| Qcache_free_blocks | 7499 |

| Qcache_free_memory | 190662000 |

| Qcache_hits | 1888430018 |

| Qcache_inserts | 1014096388 |

| Qcache_lowmem_prunes | 106071885 |

| Qcache_not_cached | 7951123988 |

| Qcache_queries_in_cache | 19315 |

| Qcache_total_blocks | 47870 |

+-------------------------+------------+

● 「Qcache_free_blocks」:Query Cache 中目前還有多少剩餘的blocks。若是該值顯示較大,則說明Query Cache 中的內存碎片較多了,可能須要尋找合適的機會進行整理()。

● 「Qcache_free_memory」:Query Cache 中目前剩餘的內存大小。經過這個參數咱們能夠較爲準確的觀察出當前系統中的Query Cache 內存大小是否足夠,是須要增長仍是過多了;

● 「Qcache_hits」:多少次命中。經過這個參數咱們能夠查看到Query Cache 的基本效果;

● 「Qcache_inserts」:多少次未命中而後插入。經過「Qcache_hits」和「Qcache_inserts」兩個參數咱們就能夠算出Query Cache 的命中率了:Query Cache 命中率= Qcache_hits / ( Qcache_hits + Qcache_inserts );

● 「Qcache_lowmem_prunes」:多少條Query 由於內存不足而被清除出Query Cache。經過

「Qcache_lowmem_prunes」和「Qcache_free_memory」相互結合,可以更清楚的瞭解到咱們系統中Query Cache 的內存大小是否真的足夠,是否很是頻繁的出現由於內存不足而有Query 被換出

● 「Qcache_not_cached」:由於query_cache_type 的設置或者不能被cache 的Query 的數量;

● 「Qcache_queries_in_cache」:當前Query Cache 中cache 的Query 數量;

● 「Qcache_total_blocks」:當前Query Cache 中的block 數量;

Query Cache 的限制


Query Cache 因爲存放的都是邏輯結構的Result Set,而不是物理的數據頁,因此在性能提高的同時,也會受到一些特定的限制。

a) 5.1.17 以前的版本不能Cache 幫定變量的Query,可是從5.1.17 版本開始,Query Cache 已經開始支持幫定變量的Query 了;

b) 全部子查詢中的外部查詢SQL 不能被Cache;

c) 在Procedure,Function 以及Trigger 中的Query 不能被Cache;

d) 包含其餘不少每次執行可能獲得不同結果的函數的Query 不能被Cache。

鑑於上面的這些限制,在使用Query Cache 的過程當中,建議經過精確設置的方式來使用,僅僅讓合適的表的數據能夠進入Query Cache,僅僅讓某些Query 的查詢結果被Cache。

相關文章
相關標籤/搜索