我刚开始涉足 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
.这里有一些快速演示。
基线:
运行查询...
总块数增加了 3,插入数增加了 1,缓存中的查询数增加了 1。
运行相同的查询,但大小写不同...
此查询被单独缓存。总块只增加了 2,因为我们已经为表分配了一个块。
现在,我们更改表中的不同行。
查询和表引用都从缓存中失效,只剩下 1 个连续的空闲块,所有的缓存内存都被释放,所有的空闲空间都合并在一个块中。
MySQL 不会在缓存中存储不确定的查询——例如
SELECT NOW();
你明确告诉它不要缓存的任何查询。SELECT SQL_NO_CACHE ...
是告诉服务器不要将结果存储在缓存中的指令。当缓存在后续执行中为您提供看似快速的响应时,它对于对查询的真实执行时间进行基准测试很有用。