MySQL的Query Cache原理分析

來源:互聯網
上載者:User

原理
QueryCache(下面簡稱QC)是根據SQL語句來cache的。一個SQL查詢如果以select開頭,那麼MySQL伺服器將嘗試對其使用QC。每個Cache都是以SQL文本作為key來存的。在應用QC之前,SQL文本不會被作任何處理。也就是說,兩個SQL語句,只要相差哪怕是一個字元(例如大小寫不一樣;多一個空格等),那麼這兩個SQL將使用不同的一個CACHE。
不過SQL文本有可能會被用戶端做一些處理。例如在官方的命令列用戶端裡,在發送SQL給伺服器之前,會做如下處理:

過濾所有注釋
去掉SQL文本前後的空格,TAB等字元。注意,是文本前面和後面的。中間的不會被去掉。

下面的三條SQL裡,因為SELECT大小寫關係,最後一條和其他兩條在QC裡肯定是用的不一樣的儲存位置。而第一條和第二條,區別在於後者有個注釋,在不同用戶端,會有不一樣的結果。所以,保險起見,請盡量不要使用動態注釋。在PHP的mysql擴充裡,SQL的注釋是不會被去掉的。也就是三條SQL會被儲存在三個不同的緩衝裡,雖然它們的結果都是一樣的。

select * FROM people where name='surfchen';
select * FROM people where /*hey~*/name='surfchen';
SELECT * FROM people where name='surfchen';
目前只有select語句會被cache,其他類似show,use的語句則不會被cache。

因為QC是如此前端,如此簡單的一個緩衝系統,所以如果一個表被更新,那麼和這個表相關的SQL的所有QC都會被失效。假設一個聯集查詢裡涉及到了表A和表B,如果表A或者表B的其中一個被更新(update或者delete),這個查詢的QC將會失效。

也就是說,如果一個表被頻繁更新,那麼就要考慮清楚究竟是否應該對相關的一些SQL進行QC了。一個被頻繁更新的表如果被應用了QC,可能會加重資料庫的負擔,而不是減輕負擔。我一般的做法是預設開啟QC,而對一些涉及頻繁更新的表的SQL語句加上SQL_NO_CACHE關鍵詞來對其禁用CACHE。這樣可以儘可能避免不必要的記憶體操作,儘可能保持記憶體的連續性。

那些查詢很分散的SQL語句,也不應該使用QC。例如用來查詢使用者和密碼的語句——“select pass from user where name='surfchen'”。這樣的語句,在一個系統裡,很有可能只在一個使用者登陸的時候被使用。每個使用者的登陸所用到的查詢,都是不一樣的SQL文本,QC在這裡就幾乎不起作用了,因為緩衝的資料幾乎是不會被用到的,它們只會在記憶體裡佔地方。

儲存塊
在本節裡“儲存塊”和“block”是同一個意思
QC緩衝一個查詢結果的時候,一般情況下不是一次性地分配足夠多的記憶體來緩衝結果的。而是在查詢結果獲得的過程中,逐Block Storage。當一個儲存塊被填滿之後,一個新的儲存塊將會被建立,並分配記憶體(allocate)。單個儲存塊的記憶體配置大小通過query_cache_min_res_unit參數控制,預設為4KB。最後一個儲存塊,如果不能被全部利用,那麼沒使用的記憶體將會被釋放。如果被緩衝的結果很大,那麼會可能會導致分配記憶體操作太頻繁,系統系能也隨之下降;而如果被緩衝的結果都很小,那麼可能會導致記憶體片段過多,這些片段如果太小,就很有可能不能再被分配使用。

除了查詢結果需要儲存塊之外,每個SQL文本也需要一個儲存塊,而涉及到的表也需要一個儲存塊(表的儲存塊是所有線程共用的,每個表只需要一個儲存塊)。儲存塊總數量=查詢結果數量*2+涉及的資料庫表數量。也就是說,第一個緩衝產生的時候,至少需要三個儲存塊:表資訊儲存塊,SQL文本儲存塊,查詢結果儲存塊。而第二個查詢如果用的是同一個表,那麼最少只需要兩個儲存塊:SQL文本儲存塊,查詢結果儲存塊。

通過觀察Qcache_queries_in_cache和Qcache_total_blocks可以知道平均每個緩衝結果佔用的儲存塊。它們的比例如果接近1:2,則說明當前的query_cache_min_res_unit參數已經足夠大了。如果Qcache_total_blocks比Qcache_queries_in_cache多很多,則需要增加query_cache_min_res_unit的大小。

Qcache_queries_in_cache*query_cache_min_res_unit(sql文本和表資訊所在的block佔用的記憶體很小,可以忽略)如果遠遠大於query_cache_size-Qcache_free_memory,那麼可以嘗試減小query_cache_min_res_unit的值。

調整大小
如果Qcache_lowmem_prunes增長迅速,意味著很多緩衝因為記憶體不夠而被釋放,而不是因為相關表被更新。嘗試加大query_cache_size,盡量使Qcache_lowmem_prunes零增長。
啟動參數
show variables like 'query_cache%'可以看到這些資訊。
query_cache_limit:如果單個查詢結果大於這個值,則不Cache
query_cache_size:分配給QC的記憶體。如果設為0,則相當于禁用QC。要注意QC必須使用大約40KB來儲存它的結構,如果設定小於40KB,則相當于禁用QC。QC儲存的最小單位是1024 byte,所以如果你設定了一個不是1024的倍數的值,這個值會被四捨五入到最接近當前值的等於1024的倍數的值。
query_cache_type:0 完全禁止QC,不受SQL語句控制(另外可能要注意的是,即使這裡禁用,上面一個參數所設定的記憶體大小還是會被分配);1啟用QC,可以在SQL語句使用SQL_NO_CACHE禁用;2可以在SQL語句使用SQL_CACHE啟用。
query_cache_min_res_unit:每次給QC結果分配記憶體的大小
狀態
show status like 'Qcache%'可以看到這些資訊。
Qcache_free_blocks:當一個表被更新之後,和它相關的cache blocks將被free。但是這個block依然可能存在隊列中,除非是在隊列的尾部。這些blocks將會被統計到這個值來。可以用FLUSH QUERY CACHE語句來清空free blocks。
Qcache_free_memory:可用記憶體,如果很小,考慮增加query_cache_size
Qcache_hits:自mysql進程啟動起,cache的命中數量
Qcache_inserts:自mysql進程啟動起,被增加進QC的數量
Qcache_lowmem_prunes:由於記憶體過少而導致QC被刪除的條數。加大query_cache_size,儘可能保持這個值0增長。
Qcache_not_cached:自mysql進程啟動起,沒有被cache的唯讀查詢數量(包括select,show,use,desc等)
Qcache_queries_in_cache:當前被cache的SQL數量
Qcache_total_blocks:在QC中的blocks數。一個query可能被多個blocks儲存,而這幾個blocks中的最後一個,未用滿的記憶體將會被釋放掉。例如一個QC結果要佔6KB記憶體,如果query_cache_min_res_unit是4KB,則最後將會產生3個blocks,第一個block用來儲存sql語句文本,這個不會被統計到query+cache_size裡,第二個block為4KB,第三個block為2KB(先allocate4KB,然後釋放多餘的2KB)。每個表,當第一個和它有關的SQL查詢被CACHE的時候,會使用一個block來儲存表資訊。也就是說,block會被用在三處地方:表資訊,SQL文本,查詢結果。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.