顾名思义,mysql query cache 就是用来缓存和 query 相关的数据的。具体来说,query cache 缓存了我们客户端提交给 mysql 的 select 语句以及该语句的结果集。大概来讲,就是将 select 语句和语句的结果做了一个 hash 映射关系然后保存在一定的内存区域中。
在大部分的 mysql 分发版本中,query cache 功能默认都是打开的,我们可以通过调整 mysql server 的参数选项打开该功能。主要由以下5个参数构成:
query_cache_limit:允许 cache 的单条 query 结果集的最大容量,默认是1mb,超过此参数设置的 query 结果集将不会被 cache query_cache_min_res_unit:设置 query cache 中每次分配内存的最小空间大小,也就是每个 query 的 cache 最小占用的内存空间大小 query_cache_size:设置 query cache 所使用的内存大小,默认值为0,大小必须是1024的整数倍,如果不是整数倍,mysql 会自动调整降低最小量以达到1024的倍数 query_cache_type:控制 query cache 功能的开关,可以设置为0(off),1(on)和2(demand)三种,意义分别如下: 0(off):关闭 query cache 功能,任何情况下都不会使用 query cache 1(on):开启 query cache 功能,但是当 select 语句中使用的 sql_no_cache 提示后,将不使用query cache 2(demand):开启 query cache 功能,但是只有当 select 语句中使用了 sql_cache 提示后,才使用 query cache query_cache_wlock_invalidate:控制当有写锁定发生在表上的时刻是否先失效该表相关的 query cache,如果设置为 1(true),则在写锁定的同时将失效该表相关的所有 query cache,如果设置为0(false)则在锁定时刻仍然允许读取该表相关的 query cache。 q:query cache 如何处理子查询的?
a:这是我遇到的最为常见的一个问题。其实 query cache 是以客户端请求提交的 query 为对象来处理的,只要客户端请求的是一个 query,无论这个 query 是一个简单的单表查询还是多表 join,亦或者是带有子查询的复杂 sql,都被当作成一个 query,不会被分拆成多个 query 来进行 cache。所以,存在子查询的复杂 query 也只会产生一个cache对象,子查询不会产生单独的cache内容。union[all] 类型的语句也同样如此。
q:query cache 是以 block 的方式存储的数据块吗?
a:不是,query cache 中缓存的内容仅仅只包含该 query 所需要的结果数据,是结果集。当然,并不仅仅只是结果数据,还包含与该结果相关的其他信息,比如产生该 cache 的客户端连接的字符集,数据的字符集,客户端连接的 default database等。不是,query cache 中缓存的内容仅仅只包含该 query 所需要的结果数据,是结果集。当然,并不仅仅只是结果数据,还包含与该结果相关的其他信息,比如产生该 cache 的客户端连接的字符集,数据的字符集,客户端连接的 default database等。
q:query cache 为什么效率会非常高,即使所有数据都可以 cache 进内存的情况下,有些时候也不如使用 query cache 的效率高?
a:query cache 的查找,是在 mysql 接受到客户端请求后在对 query 进行权限验证之后,sql 解析之前。也就是说,当 mysql 接受到客户端的sql后,仅仅只需要对其进行相应的权限验证后就会通过 query cache 来查找结果,甚至都不需要经过 optimizer 模块进行执行计划的分析优化,更不许要发生任何存储引擎的交互,减少了大量的磁盘 io 和 cpu 运算,所以效率非常高。
q:客户端提交的 sql 语句大小写对 query cache 有影响吗?
a:有,由于 query cache 在内存中是以 hash 结构来进行映射,hash 算法基础就是组成 sql 语句的字符,所以必须要整个 sql 语句在字符级别完全一致,才能在 query cache 中命中,即使多一个空格也不行。
q:一个 sql 语句在 query cache 中的内容,在什么情况下会失效?
a:为了保证 query cache 中的内容与是实际数据绝对一致,当表中的数据有任何变化,包括新增,修改,删除等,都会使所有引用到该表的 sql 的 query cache 失效。
q:为什么我的系统在开启了 query cache 之后整体性能反而下降了?
a:当开启了 query cache 之后,尤其是当我们的 query_cache_type 参数设置为 1 以后,mysql 会对每个 select 语句都进行 query cache 查找,查找操作虽然比较简单,但仍然也是要消耗一些 cpu 运算资源的。而由于 query cache 的失效机制的特性,可能由于表上的数据变化比较频繁,大量的 query cache 频繁的被失效,所以 query cache 的命中率就可能比较低下。所以有些场景下,query cache 不仅不能提高效率,反而可能造成负面影响。
q:如何确认一个系统的 query cache 的运行是否健康,命中率如何,设置量是否足够?
a:mysql 提供了一系列的 global status 来记录 query cache 的当前状态,具体如下:
qcache_free_blocks:目前还处于空闲状态的 query cache 中内存 block 数目 qcache_free_memory:目前还处于空闲状态的 query cache 内存总量 qcache_hits:query cache 命中次数 qcache_inserts:向 query cache 中插入新的 query cache 的次数,也就是没有命中的次数 qcache_lowmem_prunes:当 query cache 内存容量不够,需要从中删除老的 query cache 以给新的 cache 对象使用的次数 qcache_not_cached:没有被 cache 的 sql 数,包括无法被 cache 的 sql 以及由于 query_cache_type 设置的不会被 cache 的 sql qcache_queries_in_cache:目前在 query cache 中的 sql 数量 qcache_total_blocks:query cache 中总的 block 数量 可以根据这几个状态计算出 cache 命中率,计算出 query cache 大小设置是否足够,总的来说,我个人不建议将 query cache 的大小设置超过256mb,这也是业界比较常用的做法。
q:mysql cluster 是否可以使用 query cache?
a:其实在我们的生产环境中也没有使用 mysql cluster,所以我也没有在 mysql cluster 环境中使用 query cache 的实际经验,只是 mysql 文档中说明确实可以在 mysql cluster 中使用 query cache。从 mysql cluster 的原理来分析,也觉得应该可以使用,毕竟 sql 节点和数据节点比较独立,各司其职,只是 cache 的失效机制会要稍微复杂一点。
