原文:http://dinglin.iteye.com/blog/1625195
有同學在問 MySQL中 QueryCache(QC)的鎖是 “全域鎖”還是 “表鎖”。這裡簡要說明一下。
1、 QC基本概念
這個是實現在MySQL層(非引擎層)的一個記憶體結構,基本規則是將滿足一定條件的查詢結果緩衝在記憶體中,若同樣的查詢再執行第二次,而且緩衝沒有失效,則可以直接返回查詢結果,無需到引擎擷取資料。
幾個說明:
a) QC的結構是hash,key為查詢字串的原文,因此若想命中QC,要求查詢語句與之前的一模一樣,包括大小寫必須一致、不能增減空格等等。
b) Qc可以緩衝一個表中的多個查詢語句和結果。
c) 對一個表的DML或DDL操作都會將與這個表有關的緩衝都從QC中刪除。
2、 鎖模型
於是說到鎖的粒度。整個QC在記憶體中只有一個執行個體Query_cache query_cache;
我們來看上面c中說到的失效邏輯的部分代碼
Cpp代碼
- void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
- {
- DEBUG_SYNC(thd, “wait_in_query_cache_invalidate1″);
-
- lock();
-
- DEBUG_SYNC(thd, “wait_in_query_cache_invalidate2″);
-
- if (query_cache_size > 0)
- invalidate_table_internal(thd, key, key_length);
-
- unlock();
- }
可以看到這裡lock()沒有參數,函數內部用一額全域訊號量COND_cache_status_changed,來控制。
因此即使兩個dml更新的是不同的表,也會由雩都要失效本表在QC中的快取項目而互鎖。
因此是“全域鎖”。
3、鎖策略
得到上述結論後我們有點擔心,作為一個全域變數,是否也會鎖住“查詢”。試想如果我們在作一個DDL時,需要失效這個表的快取項目,而這個鎖的時間就會持續很長。 這期間其他表的普通查詢,是否也會受影響。 如果是,這個損失太大了。
我們知道,查詢過程中的對QC的訪問包含兩部分 :查詢開始之前從QC中判斷當前Query的結果是否已經緩衝; 若沒有,則查詢執行完成後,(可能)需要將這個結果插入到QC中。
這兩個操作都其實也都需要對QC加鎖。這樣說來, 這個鎖的頻度如此之高,以至於我們會擔心是否會得不償失?
更新時失效快取項目是必要的操作,但查詢時對QC的操作則不是必須的。MySQL中使用try_lock的策略。簡單來說,就是在上面的兩個階段中,試圖去加鎖,若逾時,則放棄。
這個逾時時間寫死在代碼中是50ms,所以若一個 查詢期間的兩次對QC的操作都出現鎖逾時,則這個查詢會額外耗費100ms的時間。
當然若是dml操作需要失效QC中的項,而碰上鎖等待,就必須等了。
4、小結
從上面描述中可以得出一些結論,對於更新操作比較小的服務,開啟QC的效果會不錯,因為查詢期間使用的try_lock策略使得不會出現查詢在QC階段互鎖的問題。(這個50ms如果覺得太大,可以在源碼中去掉個0)。
當然若是更新頻繁的表,還是建議關閉QC。現在主幹版本上用參數關閉QC不夠徹底,還是會有一些cpu消耗。有興趣的同學可以看 這篇 。