【MySQL】Query Cache 优化

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

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

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

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。

相关文章
相关标签/搜索