首页 > Mysql > MySQL Query Cache使用

MySQL Query Cache使用

2011年5月26日 发表评论 阅读评论

介绍:
Query Cache保存了SELECT语句的完整结果集,如果缓存命中,那么会跳过解析,预处理,优化,执行等步骤,直接返回结果给客户端。SELECT查询中使 用过的表也会被缓存,如果表发生了变化,那么和此表相关的SELECT查询缓存将会失效,这种方式有些粗暴,毕竟表的改变不一定对SELECT查询结果有 影响,但是这种简单的方式对复杂的系统还是很重要的。

如何命中:
MySQL检查缓存的方法很简单,就是去查找一个表,查询的键为用当前SELECT语句,数据库名称,客户端协议的版本,以及其他因素生成的一个哈希值。

MySQL会精确地使用客户端传过来的SELECT查询语句以及其他数据,两条语句中只要字符大小写,空格,注释有一点点不同,那么MySQL就认 为这是两个不同的查询。所以在书写SQL语句时候要保持统一的风格,因为这不仅可以使代码更加的统一,无形中还提高了性能,何乐而不为呢?

以下两条语句MySQL会认为是不同的查询:
SELECT * FROM t WHERE ….
select * from t where ….

不能被缓存的查询:
MySQL对结果不确定的查询不会进行缓存,因为这个本来就无法缓存。如果查询语句中存在NOW()等类似的返回值不确定的函数调用,那么不能被缓存,除 了这些函数以外,用户自定义函数,存储函数,用户自定义变量,临时表,MySQL系统表等如果出现在SELECT语句中,这个查询结果集也不能被 MySQL缓存;子查询,视图,存储过程以及准备语句(Prepared Statement)也不可以被缓存。

Query Cache 是以客户端请求提交的 Query 为对象来处理的,只要客户端请求的是一个 Query,无论这个 Query 是一个简单的单表查询还是多表 Join,亦或者是带有子查询的复杂 SQL,都被当作成一个 Query,不会被分拆成多个 Query 来进行 Cache。所以,存在子查询的复杂 Query 也只会产生一个Cache对象,子查询不会产生单独的Cache内容。UNION[ALL] 类型的语句也同样如此。

缓存的开销:
首先SELECT查询要在缓存中查找对应的缓存结果集;其次如果这个查询是可以被缓存的,并且这个查询结果还没有被缓存,那么需要把此次查询的结果集写入到缓存中;最后,如果对表进行写操作的话,除了基本的IO操作之外,还要把和此表相关的查询缓存失效。

查询缓存可以减少结果集产生的时间,但它不会减少结果集发送到客户端的时间,而这个时间可能往往是性能的瓶颈。

最适合使用缓存的查询是那种需要很多资源来产生结果,但是不需要很多的空间来存储结果的查询,这种查询在存储,返回,以及失效等方面的开销都很小,比如COUNT()操作就适合用查询缓存。

如果对一张表进行比较平均的读写操作,那么查询缓存就不会起很大的作用了。因为写入数据不停地使缓存失效,然后查询又不停地把结果集进行缓存,这种情况下给系统根本带来不了多大的性能提升。

内存管理:
MySQL把结果集缓存在内存中,内存池的大小为query_cache_size设置的值减去自身需要的40KB。需要缓存查询结果的时候,服务器会为 查询分配一块内存空间,这个空间的大小至少为query_cache_min_res_unit的值。服务器会把查询结果集保存在这个内存块中,如果这个 块满了之后还有结果没有保存,那么服务器就会再分配一个内存块来保存余下的结果,如果保存完成之后,发现这个内存块还有空间没被使用,那么系统会把这剩下 的空间合并到缓存的可用空间中去,以供下次分配内存块时候使用。

如果有两个并发的查询在进行,并且其结果都是可以被缓存的,那么系统会分配两个内存块分别对其结果进行缓存,如果这两个结果集缓存完毕之后,分配的 两个内存块之间留下了一个小于query_cache_min_res_unit值的内存空间,那么系统是无法利用这个空间的,这就产生了碎片。碎片是个 经典的问题,这里能力有限不做详解。碎片最严重的情况就是每隔一个存储块就有一个碎片,如果 Qcache_free_blocks=Qcache_total_blocks/2那么就要进行优化了。

FLUSH QUERY CACHE命令可以把所有的存储块向上移动,把所有的空闲块向下移动合并到可用内存中去。它在运行时候会锁定整个服务器,阻止访问缓存,但通常这个操作很 快,除非缓存的内容很大。这个操作不会删除缓存的内容,会把缓存清空的命令是:RESET QUERY CACHE

因为每个查询服务器不会在内存中生成完整的结果集后再发送到客户端,而是生成一行结果就发送一行结果,因为这样效率会很高。由于服务器这种行为,导致服务器自己在缓存结果集的时候不能精确地分配内存块的大小。

除了查询结果需要存储块之外,每个SQL文本也需要一个存储块,而涉及到的表也需要一个存储块(表的存储块是所有线程共享的,每个表只需要一个存储 块)。 存储块总数量=查询结果数量*2+涉及的数据库表数量。也就是说,第一个缓存生成的时候,至少需要三个存储块:表信息存储块,SQL文本存储块,查询结果 存储块。而第二个查询如果用的是同一个表,那么最少只需要两个存储块:SQL文本存储块,查询结果存储块。
和内存相关的状态变量:
mysql> show status like ‘Qcache%’;

	+-------------------------+----------+
	| Variable_name           | Value    |
	+-------------------------+----------+
	| Qcache_free_blocks      | 1        | 缓存中有多少未被使用空闲的内存块
	| Qcache_free_memory      | 16768400 | 可用的缓存空间
	| Qcache_hits             | 0        | 缓存命中的次数
	| Qcache_inserts          | 0        | 被缓存的查询次数,也就是没有命中的次数
	| Qcache_lowmem_prunes    | 0        | 由于内存不足导致被删除的缓存条目数量
	| Qcache_not_cached       | 64       | 无法被缓存的查询的数量
	| Qcache_queries_in_cache | 0        | 当前被cache的查询数量
	| Qcache_total_blocks     | 1        | 当前使用的内存块的数量
	+-------------------------+----------+

Qcache_queries_in_cache和Qcache_total_blocks关系:
一个查询的结果集可能被多个内存块存储,而这几个块中的最后一个未用满的内存将会被释放掉。
例如一个缓存结果要占6KB内存,如果query_cache_min_res_unit是4KB,则最后将会生成3个内存块,第一个块用来存储sql语 句文本,这个不会被统计到query_cache_size里,第二个块为4KB,第三个块为2KB(先分配4KB,然后释放多余的2KB)。在碎片很少 的情况下,Qcache_total_blocks大概是Qcache_queries_in_cache的二倍左右。

相关的配置:

* query_cache_type:
OFF 不对SELECT结果集缓存
ON 对SELECT结果集缓存 (默认)
DEMAND 只有当SELECT语句中显示写明“SQL_CACHE”时候才对结果集缓存,例如“SELECT SQL_CACHE id FROM t WHERE …”
* query_cache_size:
分配给用来缓存的内存空间大小,单位为字节(Byte),必须为1024的倍数,否则会被MySQL向下转换为1024的倍数,默认值为0,表示关闭缓存。设置的值必须要大于40KB(Query Cache自身使用),如果小于40KB则会报错!
注意:即使query_cache_type的值设置为OFF,query_cache_size中的内存还是会被分配,所以如果要关闭缓存,要把query_cache_size设置为0。
* query_cache_min_res_unit:
缓存块(block)的最小单位,默认值为4096(4KB),单位为字节(Byte)
* query_cache_limit:
MySQL可以缓存的最大结果集的大小,单位字节(Byte),默认值为1MB。如果SELECT查询出来的结果集大于这个值,那么MySQL将放弃缓存 这个结果集。MySQL是在服务器产生结果的时候进行缓存的,它之前无法预知这个结果集是否超出了这个限制,如果在缓存的过程中发现超过了限制,那么它会 停止缓存,并增加Qcache_not_cached这个值。如果事先知道这个情况会发生,可以通过SQL_NO_CACHE来避免这个开销。
* query_cache_wlock_invalidate:
这个选项用来设置,如果一张表被一个操作加上了Write Lock,是否允许其他的SELECT查询读取缓存中此表的内容。
FALSE,允许访问缓存中的表的内容(默认)
TRUE,不允许访问缓存中的表的内容

可以通过以下命令来查看这些变量设置情况:
mysql> show variables like ‘query_cache%’;

	+------------------------------+----------+
	| Variable_name                | Value    |
	+------------------------------+----------+
	| query_cache_limit            | 1048576  |
	| query_cache_min_res_unit     | 4096     |
	| query_cache_size             | 16777216 |
	| query_cache_type             | ON       |
	| query_cache_wlock_invalidate | OFF      |
	+------------------------------+----------+

常用的计算:

* SELECT查询次数 = Com_select + Qcache_hits(查询命中缓存会增加此变量的值)+ queries with errors found by parser(查询有错误)
* Com_select = Qcache_inserts(没有命中缓存的数量) + Qcache_not_cached(无法被缓存的数量) + queries with errors found during columns/rights check(错误查询的数量)
* Qcache命中率 = Qcache_hits / (Com_select + Qcache_hits)
* Qcache碎片率 = Qcache_free_blocks / Qcache_total_blocks
* Query结果集平均大小 = (query_cache_size – Qcache_free_memory) / Qcache_queries_in_cache

分类: Mysql 标签:
  1. 本文目前尚无任何评论.