MySQL查询缓存保存查询返回的完整结果。当查询命中该缓存,会立刻返回结果,跳过了解析,优化和执行阶段。
查询缓存会跟踪查询中涉及的每个表,如果这写表发生变化,那么和这个表相关的所有缓存都将失效。
但是随着服务器功能的强大,查询缓存也可能成为整个服务器的资源竞争单点。
如何来判断打开查询缓存的好坏呢,就是本篇的内容~~~
一、如何判断命中缓存
缓存存放在一个引用表中,通过一个哈希值引用,这个哈希值包括查询本身,数据库,客户端协议的版本等,任何字符上的不同,例如空格,注释都会导致缓存不命中。
当查询中有一些不确定的数据时,是不会缓存的,比方说now(),current_date(),自定义函数,存储函数,用户变量,字查询等。所以这样的查询也就不会命中缓存,但是还会去检测缓存的,因为查询缓存在解析SQL之前,所以MySQL并不知道查询中是否包含该类函数,只是不缓存,自然不会命中。
打开Qcache对读和写都会带来额外的消耗:
a、读查询开始之前必须检查是否命中缓存。
b、如果读查询可以缓存,那么执行完之后会写入缓存。
c、当向某个表写入数据的时候,必须将这个表所有的缓存设置为失效,如果缓存空间很大,则消耗也会很大,可能使系统僵死一段时间,因为这个操作是靠全局锁操作来保护的。
对InnoDB表,当修改一个表时,设置了缓存失效,但是多版本特性会暂时将这修改对其他事务屏蔽,在这个事务提交之前,所有查询都无法使用缓存,直到这个事务被提交,所以长时间的事务,会大大降低查询缓存的命中。
二、Qcache如何使用内存
MySQL用于查询的缓存的内存被分成一个个变长数据块,用来存储类型,大小,数据等信息。
当服务器启动的时候,会初始化缓存需要的内存,是一个完整的空闲块。当查询结果需要缓存的时候,先从空闲块中申请一个数据块大于参数query_cache_min_res_unit的配置,即使缓存数据很小,申请数据块也是这个,因为查询开始返回结果的时候就分配空间,此时无法预知结果多大。
分配内存块需要先锁住空间块,所以操作很慢,MySQL会尽量避免这个操作,选择尽可能小的内存块,如果不够,继续申请,如果存储完时有空余则释放多余的。
但是如果并发的操作,余下的需要回收的空间很小,小于query_cache_min_res_unit,不能再次被使用,就会产生碎片。如图:
三、什么情况缓存发挥作用
开启查询缓存可能是一个查询提高性能,但也可能影响其他查询。
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算是比较好的。
四、如何配置和维护查询缓存
先来看看我们的变量:
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 来关闭查询缓存。
五、InnoDB的查询缓存
事务是否可以访问查询缓存取决于当前事务的ID,以及对应表上是否有锁。
当表上任何锁的时候,那么这个表的任何查询都无法被缓存的。
a、所有大于表内存字典中的事务ID的才可以使用查询缓存。例如当前系统事务ID是5,那么1-4的事务是不能使用查询缓存的。
b、实际上表的ID是系统版本号,所以当前事务自身后续的更新操作也无法都去和修改查询缓存。
六、总结
合理配置参数,或者使用SQL_CACHE和SQL_NO_CACHE来控制某个select是否需要进行缓存。
对于写密集型的应用,建议关闭查询缓存。
DONE!!!