Qcache_free_memory 未滿但我得到了很多 Qcache_lowmem_prunes
我剛開始涉足 CMS 的查詢記憶體。
誰能告訴我(或至少給出一個很好的猜測)為什麼當超過一半是免費的時我會得到很多?
Qcache_lowmem_prunes``Qcache_free_memory
query_cache_size=512M query_cache_limit=1M
這是大約12小時後的樣子
show status like '%qcach%'; +-------------------------+-----------+ | Variable_name | Value | +-------------------------+-----------+ | Qcache_free_blocks | 10338 | | Qcache_free_memory | 297348320 | | Qcache_hits | 10254104 | | Qcache_inserts | 6072945 | | Qcache_lowmem_prunes | 725279 | | Qcache_not_cached | 2237603 | | Qcache_queries_in_cache | 48119 | | Qcache_total_blocks | 111346 | +-------------------------+-----------+
這就是它的照顧方式
flush query cache
;show status like '%qcach%'; +-------------------------+-----------+ | Variable_name | Value | +-------------------------+-----------+ | Qcache_free_blocks | 1 | | Qcache_free_memory | 443559256 | | Qcache_hits | 10307015 | | Qcache_inserts | 6115890 | | Qcache_lowmem_prunes | 725279 | | Qcache_not_cached | 2249405 | | Qcache_queries_in_cache | 26455 | | Qcache_total_blocks | 54490 | +-------------------------+-----------+
查詢記憶體是一個非常好的特性,但不要太在意它,也不要試圖讓它太大。了解它的一些內部結構可能會在這方面有所幫助。
查詢記憶體一開始是一大塊連續的可用記憶體。然後從這個大塊中雕刻出“塊”:
- 每個記憶體查詢佔用一個塊
- 它的伴隨結果集需要一個塊
- 任何記憶體查詢引用的每個表(無論記憶體中有多少個引用該表的查詢)也需要一個塊,每個表一個塊。
塊大小是動態的,但伺服器為
query_cache_min_res_unit
每個塊分配最少的字節數,典型的預設值為 4096 字節。任何時候從記憶體中刪除查詢、它們的伴隨結果和表引用,無論是通過更改基礎表而變得無效,還是通過修剪為新查詢騰出空間,這都會留下新的漏洞,無論這些塊的大小有多大,並且“空閒塊”的數量通常會增加……雖然如果兩個或更多連續塊被釋放,“空閒塊”的數量只會增加 1,如果新的 -釋放的塊與一個已經空閒的塊是連續的——那個空閒塊的大小變得更大了。查詢記憶體中任何打開的空閒記憶體塊都計為 1 個空閒塊。
當然,一個小於 的空閒塊
query_cache_min_res_unit
根本不會被使用。所以,查詢記憶體碎片。如果伺服器想要記憶體一個新的查詢並且無法安排足夠大小的空閒塊(該描述看似簡單,因為底層算法很複雜),則必須修剪其他東西……那就是你的
Qcache_lowmem_prunes
. 有一個“最近最少使用”(LRU)算法來決定修剪什麼。問一下為什麼伺服器不對記憶體進行碎片整理是明智的……但這沒有意義。查詢記憶體在可能時會有所幫助,但它根本不是戰略性的。您不想將處理時間(尤其是花費在全域鎖上的時間)投入到不必要的維護任務上。
伺服器花時間重新排列——碎片整理——查詢記憶體中的記憶體會適得其反,因為記憶體的結果是不斷變化的,而記憶體的重點是提高性能。
全域鎖是您不想使用過大查詢記憶體的一個很好的理由……伺服器將在那里花費太多時間,因為查詢等待輪到它們查看它們是否碰巧被記憶體並且您的性能會受到影響.
但
qcache_free_blocks
它本質上是自由空間碎片的一個指標。現在查詢記憶體中存在許多不連續的可用記憶體塊。對於要插入記憶體的新查詢,必須有足夠大的空閒空間塊來包含查詢、其結果以及(有時)其表引用。如果沒有,那麼必須有別的東西……這就是你所看到的。再次注意,可用空間不一定必須是連續的(從我通過閱讀原始碼可以看出),但當出現碎片時,並不是每個洞都會被填滿。但是對於給定的工作負載,碎片往往會隨著時間的推移而趨於平穩,因為通常沒有任何東西會像您預期的那樣長時間保留在查詢記憶體中。
這是因為,在某些方面,查詢記憶體非常簡單。
每當記憶體查詢引用的表中的數據發生更改時,涉及該表的所有查詢都會從記憶體中刪除——即使更改不會影響記憶體結果。如果表發生更改但沒有更改,這甚至是正確的,例如回滾的 InnoDB 事務。引用該表的查詢記憶體條目已被清除。
此外,在伺服器實際解析查詢之前,會檢查每個傳入查詢的查詢記憶體。唯一會匹配的是另一個完全相同的查詢,逐個字節。
SELECT * FROM my_table
並且select * from my_table
不是逐字節相同的,因此查詢記憶體沒有意識到它們是相同的查詢。
FLUSH QUERY CACHE
不會清空查詢記憶體。它對查詢記憶體進行碎片整理,這就是為什麼Qcache_free_blocks
變為“1”的原因。所有可用空間都已合併。
RESET QUERY CACHE
實際上刷新(清除所有內容)查詢記憶體。
FLUSH STATUS
清除計數器,但這不是您想要正常執行的操作,因為這會將SHOW STATUS
.這裡有一些快速展示。
基線:
mysql> show status like '%qcache%'; +-------------------------+----------+ | Variable_name | Value | +-------------------------+----------+ | Qcache_free_blocks | 1 | | Qcache_free_memory | 67091120 | | Qcache_hits | 0 | | Qcache_inserts | 0 | | Qcache_lowmem_prunes | 0 | | Qcache_not_cached | 1 | | Qcache_queries_in_cache | 0 | | Qcache_total_blocks | 1 | +-------------------------+----------+
執行查詢…
mysql> select * from junk where id = 2;
總塊數增加了 3,插入數增加了 1,記憶體中的查詢數增加了 1。
+-------------------------+----------+ | Variable_name | Value | +-------------------------+----------+ | Qcache_free_blocks | 1 | | Qcache_free_memory | 67089584 | | Qcache_inserts | 1 | | Qcache_queries_in_cache | 1 | | Qcache_total_blocks | 4 | +-------------------------+----------+
執行相同的查詢,但大小寫不同…
mysql> SELECT * FROM junk where id = 2;
此查詢被單獨記憶體。總塊只增加了 2,因為我們已經為表分配了一個塊。
+-------------------------+----------+ | Variable_name | Value | +-------------------------+----------+ | Qcache_free_blocks | 1 | | Qcache_free_memory | 67088560 | | Qcache_inserts | 2 | | Qcache_queries_in_cache | 2 | | Qcache_total_blocks | 6 | +-------------------------+----------+
現在,我們更改表中的不同行。
mysql> update junk set things = 'items' where id = 1;
查詢和表引用都從記憶體中失效,只剩下 1 個連續的空閒塊,所有的記憶體記憶體都被釋放,所有的空閒空間都合併在一個塊中。
+-------------------------+----------+ | Variable_name | Value | +-------------------------+----------+ | Qcache_free_blocks | 1 | | Qcache_free_memory | 67091120 | | Qcache_queries_in_cache | 0 | | Qcache_total_blocks | 1 | +-------------------------+----------+
MySQL 不會在記憶體中儲存不確定的查詢——例如
SELECT NOW();
你明確告訴它不要記憶體的任何查詢。SELECT SQL_NO_CACHE ...
是告訴伺服器不要將結果儲存在記憶體中的指令。當記憶體在後續執行中為您提供看似快速的響應時,它對於對查詢的真實執行時間進行基準測試很有用。