高性能的MySQL(7)Query Cache 详解

  1. 云栖社区>
  2. 博客>
  3. 正文

高性能的MySQL(7)Query Cache 详解

技术小美 2017-11-09 16:19:00 浏览516
展开阅读全文

MySQL查询缓存保存查询返回的完整结果。当查询命中该缓存,会立刻返回结果,跳过了解析,优化和执行阶段。

查询缓存会跟踪查询中涉及的每个表,如果这写表发生变化,那么和这个表相关的所有缓存都将失效。

但是随着服务器功能的强大,查询缓存也可能成为整个服务器的资源竞争单点。

如何来判断打开查询缓存的好坏呢,就是本篇的内容~~~

一、如何判断命中缓存

缓存存放在一个引用表中,通过一个哈希值引用,这个哈希值包括查询本身,数据库,客户端协议的版本等,任何字符上的不同,例如空格,注释都会导致缓存不命中。

当查询中有一些不确定的数据时,是不会缓存的,比方说now(),current_date(),自定义函数,存储函数,用户变量,字查询等。所以这样的查询也就不会命中缓存,但是还会去检测缓存的,因为查询缓存在解析SQL之前,所以MySQL并不知道查询中是否包含该类函数,只是不缓存,自然不会命中。

打开Qcache对读和写都会带来额外的消耗:

a、读查询开始之前必须检查是否命中缓存。

b、如果读查询可以缓存,那么执行完之后会写入缓存。

c、当向某个表写入数据的时候,必须将这个表所有的缓存设置为失效,如果缓存空间很大,则消耗也会很大,可能使系统僵死一段时间,因为这个操作是靠全局锁操作来保护的。

对InnoDB表,当修改一个表时,设置了缓存失效,但是多版本特性会暂时将这修改对其他事务屏蔽,在这个事务提交之前,所有查询都无法使用缓存,直到这个事务被提交,所以长时间的事务,会大大降低查询缓存的命中。

二、Qcache如何使用内存

MySQL用于查询的缓存的内存被分成一个个变长数据块,用来存储类型,大小,数据等信息。

当服务器启动的时候,会初始化缓存需要的内存,是一个完整的空闲块。当查询结果需要缓存的时候,先从空闲块中申请一个数据块大于参数query_cache_min_res_unit的配置,即使缓存数据很小,申请数据块也是这个,因为查询开始返回结果的时候就分配空间,此时无法预知结果多大。

分配内存块需要先锁住空间块,所以操作很慢,MySQL会尽量避免这个操作,选择尽可能小的内存块,如果不够,继续申请,如果存储完时有空余则释放多余的。

171752110.png

但是如果并发的操作,余下的需要回收的空间很小,小于query_cache_min_res_unit,不能再次被使用,就会产生碎片。如图:

172731767.png


三、什么情况缓存发挥作用

开启查询缓存可能是一个查询提高性能,但也可能影响其他查询。

a、对于消耗很大的查询通常都是非常适合缓存的,例如汇总计算查询。

b、对于update,delete,insert特别频繁的表不适合使用。

一个判断查询缓存是否命中的直接数据是命中率,就是查询缓存返回结果占从查询的比率,当MySQL接受到一个查询的时候要么增加Qcache_hits的值要么增加Com_select的值。

任何select没有命中缓存有以下一个可能

a、查询语句包含函数或者结果太大超过配置无法被缓存。

b、查询没有被缓存过。

c、内存不足,被新的缓存替代,或者表的数据或结构变化。

缓存碎片,内存不足,数据修改都会造成缓存失效。

可以使用Qcache_lowmem_prunes来查看有多少失效是由于内存不足导致的。

如果缓存的结果没有被任何select使用,那么这次缓存就是浪费时间和内存,可以通过查看Com_select和Qcache_inserts的值来看。如果每次查询都没有命中,然后查询结果还要写入缓存,那么这2个值应该差不多,而我们希望看到Qcache_inserts远远小于Com_select。

这里推荐比较通用的一个指标来衡量缓存的打开和关闭:

“命中和写入的比例”即Qcache_hits和Qcache_inserts的比值,最好能够达到10:1算是比较好的。


四、如何配置和维护查询缓存

先来看看我们的变量:

180053349.png

query_cache_type:

是否打开查询缓存。可以设置为ON,OFF,DEMAND。DEMAND表示只有在查询语句中使用SQL_CACHE和SQL_NO_CACHE来控制是否需要缓存。

query_cache_size:

查询缓存总空间,单位字节,必须是1024的整数倍。

query_cache_min_res_unit:

分配内存块时的最小单位。

query_cache_limit:

MySQL能够缓存的最大结果,超过的不会被缓存,Qcache_not_cached可以表示这个值。如果预先就知道结果很大不会被缓存,那么查询的时候加上SQL_NO_CACHE可以提高效率。

query_cache_wlock_invalidate:

如果某个表被锁住,是否返回缓存中的数据,默认关闭,也是建议的。


减少碎片:

合适的query_cache_min_res_unit可以减少碎片,这个参数最合适的大小和应用程序查询结果的平均大小直接相关,可以通过内存实际消耗(query_cache_size - Qcache_free_memory)除以Qcache_queries_in_cache计算平均缓存大小。

可以通过Qcache_free_blocks来观察碎片,这个值反应了剩余的空闲块,如果这个值很多,但是

Qcache_lowmem_prunes却不断增加,则说明碎片太多了。可以使用flush query cache整理碎片,重新排序,但不会清楚,清空命令是reset query cache。整理碎片期间,查询缓存无法被访问,可能导致服务器僵死一段时间,所以查询缓存不宜太大。


提高查询缓存的使用率:

如果碎片不是问题,命中率却非常低,可能是内存不足,可以通过 Qcache_free_memory 参数来查看没有使用的内存。

如果2者都没有问题,命中率依然很低,那么说明缓存不适合你的当前系统。可以通过设置

query_cache_size = 0或者query_cache_type 来关闭查询缓存。

182027370.png


五、InnoDB的查询缓存

事务是否可以访问查询缓存取决于当前事务的ID,以及对应表上是否有锁。

当表上任何锁的时候,那么这个表的任何查询都无法被缓存的。

a、所有大于表内存字典中的事务ID的才可以使用查询缓存。例如当前系统事务ID是5,那么1-4的事务是不能使用查询缓存的。

b、实际上表的ID是系统版本号,所以当前事务自身后续的更新操作也无法都去和修改查询缓存。


六、总结

合理配置参数,或者使用SQL_CACHE和SQL_NO_CACHE来控制某个select是否需要进行缓存。

对于写密集型的应用,建议关闭查询缓存。

DONE!!!






















本文转自shayang8851CTO博客,原文链接:http://blog.51cto.com/janephp/1318705,如需转载请自行联系原作者

网友评论

登录后评论
0/500
评论
技术小美
+ 关注