顧名思義,MySQL Query Cache 就是用來緩存和 Query 相關的數據的。具體來說,Query Cache 緩存了我們客戶端提交給 MySQL 的 SELECT 語句以及該語句的結果集。大概來講,就是將 SELECT 語句和語句的結果做了一個 HASH 映射關系然后保存在一定的內存區域中。
MySQL Query Cache幾個說明:
a) QC的結構是hash,key為查詢字符串的原文,因此若想命中QC,要求查詢語句與之前的一模一樣,包括大小寫必須一致、不能增減空格等等。
b) Qc可以緩存一個表中的多個查詢語句和結果。
c) 對一個表的DML或DDL操作都會將與這個表有關的緩存都從QC中刪除。
1、 鎖模型
于是說到鎖的粒度。整個QC在內存中只有一個實例Query_cache query_cache;
我們來看上面c中說到的失效邏輯的部分代碼
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中的緩存項而互鎖。
因此是“全局鎖”.
2、鎖策略
得到上述結論后我們有點擔心,作為一個全局變量,是否也會鎖住“查詢”.試想如果我們在作一個DDL時,需要失效這個表的緩存項,而這個鎖的時間就會持續很長。 這期間其他表的普通查詢,是否也會受影響。 如果是,這個損失太大了。
我們知道,查詢過程中的對QC的訪問包含兩部分 :查詢開始之前從QC中判斷當前Query的結果是否已經緩存; 若沒有,則查詢執行完成后,(可能)需要將這個結果插入到QC中。
這兩個操作都其實也都需要對QC加鎖。這樣說來, 這個鎖的頻度如此之高,以至于我們會擔心是否會得不償失?
更新時失效緩存項是必要的操作,但查詢時對QC的操作則不是必須的。MySQL中使用try_lock的策略。簡單來說,就是在上面的兩個階段中,試圖去加鎖,若超時,則放棄。
這個超時時間寫死在代碼中是50ms,所以若一個 查詢期間的兩次對QC的操作都出現鎖超時,則這個查詢會額外耗費100ms的時間。
當然若是dml操作需要失效QC中的項,而碰上鎖等待,就必須等了。
總之,當MySQL數據庫開啟了 Query Cache 之后,尤其是當我們的 query_cache_type 參數設置為 1 以后,MySQL 會對每個 SELECT 語句都進行 Query Cache 查找,查找操作雖然比較簡單,但仍然也是要消耗一些 CPU 運算資源的。而由于 Query Cache 的失效機制的特性,可能由于表上的數據變化比較頻繁,大量的 Query Cache 頻繁的被失效,所以 Query Cache 的命中率就可能比較低下。所以有些場景下,Query Cache 不僅不能提高效率,反而可能造成負面影響。