天天看點

[MySQL FAQ]系列 — 線上環境到底要不要開啟query cache

Query Cache(查詢緩存,以下簡稱QC)存儲SELECT語句及其産生的資料結果,特别适用于:頻繁送出同一個語句,并且該表資料變化不是很頻繁的場景,例如一些靜态頁面,或者頁面中的某塊不經常發生變化的資訊。QC有可能會從InnoDB Buffer Pool或者MyISAM key buffer裡讀取結果。

由于QC需要緩存最新資料結果,是以表資料發生任何變化(INSERT、UPDATE、DELETE或其他可能産生資料變化的操作),都會導緻QC被重新整理。

根據MySQL官方的測試,QC的優劣分别是:

1、如果對一個表執行簡單的查詢,但每次查詢都不一樣的話,打開QC後,性能反而下降了13%左右。但通常實際業務中,通常不會隻有這種請求,是以實際影響應該比這個小一些。

2、如果對一個隻有一行資料的表進行查詢,則可以提升238%,這個效果還是非常不錯的。

是以,如果是在一個更新頻率非常低而隻讀查詢頻率非常高的場景下,打開QC還是比較有優勢的,其他場景下,則不建議使用。而且,QC一般也維持在100MB以内就夠了,沒必要設定超過數百MB。

QC嚴格要求2次SQL請求要完全一樣,包括SQL語句,連接配接的資料庫、協定版本、字元集等因素都會影響,下面幾個例子中的SQL會被認為是完全不一樣而不會使用同一個QC記憶體塊:

mysql> set names latin1; SELECT * FROM table_name;

mysql> set names latin1; select * from table_name;

mysql> set names utf8; select * from table_name;

此外,QC也不适用于下面幾個場景:

1、子查詢或者外層查詢;

2、存儲過程、存儲函數、觸發器、event中調用的SQL,或者引用到這些結果的;

3、包含一些特殊函數時,例如:BENCHMARK()、CURDATE()、CURRENT_TIMESTAMP()、NOW()、RAND()、UUID()等等;

4、讀取mysql、INFORMATION_SCHEMA、performance_schema 庫資料的;

5、類似SELECT…LOCK IN SHARE MODE、SELECT…FOR UPDATE、SELECT..INTO OUTFILE/DUMPFILE、SELECT..WHRE…IS NULL等語句;

6、SELECT執行計劃用到臨時表(TEMPORARY TABLE);

7、未引用任何表的查詢,例如 SELECT 1+1 這種;

8、産生了 warnings 的查詢;

9、SELECT語句裡加了 SQL_NO_CACHE 關鍵字;

更加奇葩的是,MySQL在從QC中取回結果前,會先判斷執行SQL的使用者是否有全部庫、表的SELECT權限,如果沒有,則也不會使用QC。

相比下面這個,其實上面所說的都不重要。

最為重要的是,在MySQL裡QC是由一個全局鎖在控制,每次更新QC的記憶體塊都需要進行鎖定。

例如,一次查詢結果是20KB,目前 query_cache_min_res_unit 值設定為 4KB(預設值就是4KB,可調整),那麼麼本次查詢結果共需要分為5次寫入QC,每次都要鎖定,可見其成本有多高。

我們可以通過 PROFILING 功能來檢視 QC 相關的一些鎖競争,例如像下面這樣的:

  • Waiting for query cache lock
  • Waiting on query cache mutex

或者,也可以通過執行 SHOW PROCESSLIST 來看線程的狀态,例如:

• checking privileges on cached query

檢查使用者是否有權限讀取QC中的結果集

• checking query cache for query

檢查本次查詢結果是否已經存儲在QC中

• invalidating query cache entries

由于相關表資料已經修改了,是以将QC中的記憶體記錄被标記為失效

• sending cached result to client

從QC中,将緩存後的結果傳回給客戶程式

• storing result in query cache

将查詢結果緩存到QC中

如果可以頻繁看到上述幾種狀态,那麼說明目前QC基本存在比較重的競争。

說了這麼多廢話,其實核心要點就一個:

如果線上環境中99%以上都是隻讀,很少有更新,再考慮開啟QC吧,否則,就别開了。

關閉方法很簡單,有兩種:

1、同時設定選項 query_cache_type = 0 和 query_cache_size = 0;

2、如果用源碼編譯MySQL的話,編譯時增加參數 --without-query-cache 即可;

延伸閱讀:

http://www.dbasquare.com/kb/how-query-cache-can-cause-performance-problems/ http://www.percona.com/blog/2012/09/05/write-contentions-on-the-query-cache/ http://dev.mysql.com/doc/refman/5.6/en/query-cache.html