当前位置:   article > 正文

【MySQL】Query Cache 优化

query cache优化实例

注:本文内容来自MySQL大神:简朝阳http://isky000.com/ 的MySQL性能调优与架构设计》一书

谈到Query Cache,恐怕使用过MySQL 的大部分人都会或多或少有一些了解,因为在很多人看来他可以帮助我们将数据库的性能产生一个“质”的提升。但真的是这样吗?这一节我们就将如何合理的使用MySQL 的Query Cache 进行一些相应的分析并得出部分优化建议。

Query Cache 真的是“尚方宝剑”吗?

Query Cache实现原理


MySQL 的Query Cache 实现原理实际上并不是特别的复杂,简单的来说就是将客户端请求的Query语句(当然仅限于SELECT 类型的Query)通过一定的hash 算法进行一个计算而得到一个hash 值,存放在一个hash 桶中。同时将该Query 的结果集(Result Set)也存放在一个内存Cache 中的。存放Queryhash 值的链表中的每一个hash 值所在的节点中同时还存放了该Query 所对应的Result Set 的Cache 所在的内存地址,以及该Query 所涉及到的所有Table 的标识等其他一些相关信息。系统接受到任何一个SELECT 类型的Query 的时候,首先计算出其hash 值,然后通过该hash 值到Query Cache 中去匹配,如果找到了完全相同的Query,则直接将之前所Cache 的Result Set 返回给客户端而完全不需要进行后面的任何步骤即可完成这次请求。而后端的任何一个表的任何一条数据发生变化之后,也会通知QueryCache,需要将所有与该Table 有关的Query 的Cache 全部失效,并释放出之前占用的内存地址,以便后面其他的Query 能够使用。

从上面的实现原理来看,Query Cache 确实是以比较简单的实现带来巨大性能收益的功能。但是很多人可能都忽略了使用QueryCache 之后所带来的负面影响:

a) Query 语句的hash 运算以及hash 查找资源消耗。当我们使用Query Cache 之后,每条SELECT类型的Query 在到达MySQL 之后,都需要进行一个hash 运算然后查找是否存在该Query 的Cache,虽然这个hash 运算的算法可能已经非常高效了,hash 查找的过程也已经足够的优化了,对于一条Query 来说消耗的资源确实是非常非常的少,但是当我们每秒都有上千甚至几千条Query 的时候,我们就不能对产生的CPU 的消耗完全忽视了。

b) Query Cache 的失效问题。如果我们的表变更比较频繁,则会造成Query Cache 的失效率非常高。这里的表变更不仅仅指表中数据的变更,还包括结构或者索引等的任何变更。也就是说我们每次缓存到Query Cache 中的Cache 数据可能在刚存入后很快就会因为表中的数据被改变而被清除,然后新的相同Query 进来之后无法使用到之前的Cache。

c) Query Cache 中缓存的是Result Set ,而不是数据页,也就是说,存在同一条记录被Cache 多次的可能性存在。从而造成内存资源的过渡消耗。当然,可能有人会说我们可以限定Query

Cache 的大小啊。是的,我们确实可以限定Query Cache 的大小,但是这样,Query Cache 就很容易造成因为内存不足而被换出,造成命中率的下降。

对于Query Cache 的上面三个负面影响,如果单独拿出每一个影响来说都不会造成对整个系统多大的问题,并不会让大家对使用Query Cache 产生太多顾虑。但是,当综合这三个负面影响一起考虑的话,恐怕Query Cache 在很多人心目中就不再是以前的那把“尚方宝剑”了。

适度使用Query Cache

虽然Query Cache 的使用会存在一些负面影响,但是我们也应该相信其存在是必定有一定价值。我们完全不用因为Query Cache 的上面三个负面影响就完全失去对Query Cache 的信心。只要我们理解了Query Cache 的实现原理,那么我们就完全可以通过一定的手段在使用Query Cache 的时候扬长避短,重发发挥其优势,并有效的避开其劣势。

首先,我们需要根据Query Cache 失效机制来判断哪些表适合使用Query 哪些表不适合。由于QueryCache 的失效主要是因为Query 所依赖的Table 的数据发生了变化,造成Query 的Result Set 可能已经有所改变而造成相关的Query Cache 全部失效,那么我们就应该避免在查询变化频繁的Table 的Query 上使用,而应该在那些查询变化频率较小的Table 的Query 上面使用。MySQL 中针对Query Cache 有两个专用的SQL Hint(提示):SQL_NO_CACHE 和SQL_CACHE,分别代表强制不使用Query Cache 和强制使用Query Cache。我们完全可以利用这两个SQL Hint,让MySQL 知道我们希望哪些SQL 使用Query Cache 而哪些SQL 就不要使用了。这样不仅可以让变化频繁Table 的Query 浪费Query Cache 的内存,同时还可以

减少Query Cache 的检测量。

其次,对于那些变化非常小,大部分时候都是静态的数据,我们可以添加SQL_CACHE 的SQL Hint,强制MySQL 使用Query Cache,从而提高该表的查询性能。

最后,有些SQL 的Result Set 很大,如果使用Query Cache 很容易造成Cache 内存的不足,或者将之前一些老的Cache 冲刷出去。对于这一类Query 我们有两种方法可以解决,一是使用SQL_NO_CACHE 参数来强制他不使用Query Cache 而每次都直接从实际数据中去查找, 另一种方法是通过设定“query_cache_limit”参数值来控制Query Cache 中所Cache 的最大Result Set ,系统默认为1M(1048576)。当某个Query 的Result Set 大于“query_cache_limit”所设定的值的时候,QueryCache 是不会Cache 这个Query 的。

Query Cache 的相关系统参数变量和状态变量

我们首先看看Query Cache 的系统变量,可以通过执行如下命令获得MySQL 中Query Cache 相关的系统参数变量:

mysql> show variables like '%query_cache%';

+------------------------------+-----------+

| Variable_name | Value |

+------------------------------+-----------+

| have_query_cache | YES |

| query_cache_limit | 1048576 |

| query_cache_min_res_unit | 4096 |

| query_cache_size | 268435456 |

| query_cache_type | ON |

| query_cache_wlock_invalidate | OFF |

+------------------------------+-----------+

● “have_query_cache”:该MySQL 是否支持Query Cache;

● “query_cache_limit”:Query Cache 存放的单条Query 最大Result Set ,默认1M;

● “query_cache_min_res_unit”:Query Cache 每个Result Set 存放的最小内存大小,默认4k;

● “query_cache_size”:系统中用于Query Cache 内存的大小;

● “query_cache_type”:系统是否打开了Query Cache 功能;

● “query_cache_wlock_invalidate”:针对于MyISAM 存储引擎,设置当有WRITE LOCK 在某个Table 上面的时候,读请求是要等待WRITE LOCK 释放资源之后再查询还是允许直接从QueryCache 中读取结果,默认为FALSE(可以直接从Query Cache 中取得结果)。

以上参数的设置主要是“query_cache_limit”和“query_cache_min_res_unit”两个参数的设置需要做一些针对于应用的相关调整。如果我们需要Cache 的Result Set 一般都很小(小于4k)的话,可以适当将“ query_cache_min_res_unit ” 参数再调小一些, 避免造成内存的浪费,“query_cache_limit”参数则不用调整。而如果我们需要Cache 的Result Set 大部分都大于4k 的话,则最好将“query_cache_min_res_unit”调整到和Result Set 大小差不多,“query_cache_limit”的参数也应大于Result Set 的大小。当然,可能有些时候我们比较难准确的估算Result Set 的大小,那么当Result Set 较大的时候,我们也并不是非得将“query_cache_min_res_unit”设置的和每个Result Set 差不多大,是每个结果集的一半或者四分之一大小都可以,要想非常完美的完全不浪费任何内存确实也是不可能做到的。

如果我们要了解Query Cache 的使用情况,则可以通过Query Cache 相关的状态变量来获取,如通过如下命令:mysql> show status like 'Qcache%';

+-------------------------+------------+

| Variable_name | Value |

+-------------------------+------------+

| Qcache_free_blocks | 7499 |

| Qcache_free_memory | 190662000 |

| Qcache_hits | 1888430018 |

| Qcache_inserts | 1014096388 |

| Qcache_lowmem_prunes | 106071885 |

| Qcache_not_cached | 7951123988 |

| Qcache_queries_in_cache | 19315 |

| Qcache_total_blocks | 47870 |

+-------------------------+------------+

● “Qcache_free_blocks”:Query Cache 中目前还有多少剩余的blocks。如果该值显示较大,则说明Query Cache 中的内存碎片较多了,可能需要寻找合适的机会进行整理()。

● “Qcache_free_memory”:Query Cache 中目前剩余的内存大小。通过这个参数我们可以较为准确的观察出当前系统中的Query Cache 内存大小是否足够,是需要增加还是过多了;

● “Qcache_hits”:多少次命中。通过这个参数我们可以查看到Query Cache 的基本效果;

● “Qcache_inserts”:多少次未命中然后插入。通过“Qcache_hits”和“Qcache_inserts”两个参数我们就可以算出Query Cache 的命中率了:Query Cache 命中率= Qcache_hits / ( Qcache_hits + Qcache_inserts );

● “Qcache_lowmem_prunes”:多少条Query 因为内存不足而被清除出Query Cache。通过

“Qcache_lowmem_prunes”和“Qcache_free_memory”相互结合,能够更清楚的了解到我们系统中Query Cache 的内存大小是否真的足够,是否非常频繁的出现因为内存不足而有Query 被换出

● “Qcache_not_cached”:因为query_cache_type 的设置或者不能被cache 的Query 的数量;

● “Qcache_queries_in_cache”:当前Query Cache 中cache 的Query 数量;

● “Qcache_total_blocks”:当前Query Cache 中的block 数量;

Query Cache 的限制


Query Cache 由于存放的都是逻辑结构的Result Set,而不是物理的数据页,所以在性能提升的同时,也会受到一些特定的限制。

a) 5.1.17 之前的版本不能Cache 帮定变量的Query,但是从5.1.17 版本开始,Query Cache 已经开始支持帮定变量的Query 了;

b) 所有子查询中的外部查询SQL 不能被Cache;

c) 在Procedure,Function 以及Trigger 中的Query 不能被Cache;

d) 包含其他很多每次执行可能得到不一样结果的函数的Query 不能被Cache。

鉴于上面的这些限制,在使用Query Cache 的过程中,建议通过精确设置的方式来使用,仅仅让合适的表的数据可以进入Query Cache,仅仅让某些Query 的查询结果被Cache。

转载于:https://my.oschina.net/iblike/blog/144284

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/193241
推荐阅读
相关标签
  

闽ICP备14008679号