查询缓存保存了完整的SELECT结果集;这样当一个相同的查询到达时,可服务器可以跳过解析、优化、执行等阶段,直接把结果返回给客户端,但某种意义上讲,查询缓存实际上修改了应用程序请求的语义,例如,即使在LOCK TABLES的情况下,查询也能被缓存下来。
在服务器端维护了一个查找表,查找的键是查询文本、数据库和客户端协议版本及其他影响实际查询结果的因素,综合起来的哈希值,也就是说,只要客户端的查询语句有丝毫的差异,都不会命中缓存,或者查询语句相同,但不同的客户端协议版本也可能无法命中查询缓存;
此外一些有不确定结果的查询,如NOW()、CURRENT_DATE()这样的函数,查询结果不会被缓存;不会缓存的情况包括
1.用户自定义函数
2.存储函数
3.用户自定义变量
4.临时表
5.任何一个有列级权限的表的查询
在执行的过程中,MYSQL一旦发现有阻止缓存的元素存在,就将其标记为不可缓存。
在InnoDB中,当事务内部改写了表,虽然多版本机制应当对其他语句隐藏变化,但这里却会使所有引用了该表的查询缓存失效,直到事务提交之前,该表都是全局不可缓存的。
当一个请求查询缓存的时,将会阻塞所有访问缓存的查询。
---------------------------
缓存内容存储在内存中,查询缓存具有一个内存池,这在服务器启动时进行初始化,分配给查询缓存的大小是可配置的,除了查询缓存本身固定需要的40KB空间,其他都用于内存池。
由于服务器是逐行发送结果的,在执行完毕前,无法知道缓存需要的内存大小,因此,当需要缓存结果时,会先从池中获取一个至少query_cache_min_res_unit大小的块,将数据写入其中,当还有剩余数据时,就需要另外申请新块,继续加入数据,最后,当保存完毕后,如果数据块还没存满,就会将空的部分截出来,加入到剩余的空闲空间中。
在上述过程中,可能会产生很多碎片,这些碎片因为低于最小限制,无法重复使用,可以通过命令FLUSH QUERY CACHE来移除碎片。
在文件sql/sql_cache.cc中对上述过程进行了具体的描述。
----------------------------
在status变量列表里,Qcache_hits记录了命中次数,另外还有Qcache_inserts记录存储缓存的次数,Qcache_free_blocks记录了空闲块数
关于查询缓存,用到的几个全局变量包括:
1.Query_cache_type,表示缓存是否被激活,OFF、ON、DEMAND(该选项意味着只有包含了SQL_Cache选项的查询才会被缓存);
2.Query_cache_size,分配给查询缓存的大小;
3.Query_cache_min_res_unit,分配缓存块的最小值;
4.Query_cache_limit,存储结果最大值,若超过了会丢弃已缓存的值,因此当预知到可能结果超出时,最好加上选项SQL_NO_CACHE,避免额外开销;
5.Query_cache_wlock_invalidate,默认为OFF,可以读取已锁定的表的缓存数据
-----------------------------
通用的查询缓存优化方案:
1.尽量使用多个小表而非大表;
2.成批进行写入操作而非逐个执行;
3.设置合适的query_cache_size值;
4.使用SQL_CACHE和SQL_NO_CACHE来决定是否缓存查询;
5.对很多写入任务的应用程序,关闭查询缓存。