Loading... <img class=" ls-is-cached lazyloaded" title="1480776820928760.jpg" src="https://www.zsythink.net/wp-content/uploads/ueditor/php/upload/image/20161203/1480776820928760.jpg" alt="mysql.jpg" data-src="https://www.zsythink.net/wp-content/uploads/ueditor/php/upload/image/20161203/1480776820928760.jpg" style=""> 这篇文章将会总结mysql中的查询缓存。 在本博客中,”mysql”是一个系列文章,这些文章主要对mysql/mariadb的常用知识点进行了总结,每一篇博客总结的知识点有所不同,具体内容可参考mysql文章列表。 mysql文章列表直达链接:<a href="https://www.zsythink.net/archives/tag/mysql/" target="_blank" rel="noopener">mysql知识点总结</a> “查询缓存”,顾名思义,就是将查询的结果缓存下载,如果查询语句完全相同,则直接返回缓存中的结果。 如果应用程序在某个场景中,需要经常执行大量的相同的查询,而且查询出的数据不会经常被更新,那么,使用查询缓存会有一定的性能提升。 我们可以使用如下语句,查看当前服务是否开启了查询缓存功能 <img class=" lazyloaded" src="https://www.zsythink.net/wp-content/uploads/2017/01/011417_1131_1.png" alt="" data-src="https://www.zsythink.net/wp-content/uploads/2017/01/011417_1131_1.png" style=""> 可以看到,上图中,query_cache_type的值设置为了ON,也就是说,目前已经开启了查询缓存功能。 query_cache_type的值可以设置为:ON、OFF、DEMAND,分别表示已启用、已禁用、按需缓存,设置在my.cnf中即可。 have_query_cache的值为yes,表示当前数据库支持缓存功能 query_cache_limit 表示单条查询缓存的最大值,如果查询结果超过此值的大小,即使指定缓存当前结果,结果也不会被缓存,默认值为1M。 query_cache_min_res_unit表示缓存存储于内存的最小单元,默认为4k,也就是说,即使查询结果只有1k,也会占用4k内存,所以,如果此值设置的过大,会造成内存空间的浪费,如果此值设置的过小,则会频繁的分配内存单元或者频繁的回收内存单元。 query_cache_size 表示查询缓存的总大小,也就是说,内存中用于查询缓存的空间大小,如果其值为0,即使开启了查询缓存,也无法缓存,上图中,即为这种情况。 query_cache_wlock_invalidate 表示查询语句所查询的表如果被写锁锁定,是否仍然使用缓存返回结果。什么意思呢?我们来描述一个场景,因为写锁是独占的,是排他的,所以当写锁施加在对应表上的期间,如果对当前表发起查询请求,那么查询操作则需要等到写锁释放后才能进行,这是因为写锁是独占的,可是,如果对应的查询语句正好命中了这张表的缓存呢?查询请求是否就不用继续等待写锁释放而是直接从缓存中获取结果呢?此值就是用于应对这种情况的,默认值为OFF,也就是说,当此值为OFF时,即使表被施加了写锁,查询语句如果命中了对应的缓存,则会从缓存中得到结果,注意,设置为OFF表示可以从缓存返回结果,因为此值的含义为”查询缓存遭遇写锁时是否失效”,设置为OFF表示”不失效”,设置为ON表示”失效”,换句话说,如果此值设置为ON,如果表被施加了写锁,那么当写锁释放时,数据可能发生了改变,所以在表被施加写锁期间,即使此时有查询语句命中了查询缓存,也不能从缓存获取结果。那么我们可以得出结论,此值设置为OFF时,性能更好,并发能力更好,此值设置为ON时,更加安全,保证了数据的一致性。 但是,我们之前说过,只有查询语句完全相同时,缓存才能够被命中,完全相同表示大小写也相同,如下两条sql语句不算做完全相同。 <div class="enlighter-default enlighter-v-standard enlighter-t-enlighter enlighter-hover enlighter-overflow-scroll"> <div class="enlighter-toolbar"> <div class="enlighter-btn enlighter-btn-raw"></div> <div class="enlighter-btn enlighter-btn-copy"></div> <div class="enlighter-btn enlighter-btn-window"></div> </div> <div class="enlighter"> <div class=""> <div><span class="enlighter-text">select </span><span class="enlighter-g0">*</span><span class="enlighter-text"> from stu;</span></div> </div> <div class=""> <div><span class="enlighter-text">SELECT </span><span class="enlighter-g0">*</span><span class="enlighter-text"> FROM stu;</span></div> </div> </div> </div> 因为mysql收到查询请求时,会对查询语句进行hash计算,计算出其对应的hash值,通过这个hash值查找是否存在对应的缓存,所以,即使查询语句的大小写不同,也会被认为是不同的查询语句,如果当前hash码没有命中对应的缓存,mysql则会将对应的hash值存放在对应的hash表中,同时将查询结果存放在对应的缓存中,如果查询语句的hash值命中了对应缓存项,则直接从缓存中返回响应的查询结果,如果缓存对应的表中的数据发生了变化,那么查询缓存中,所有与变化的数据表有关的缓存都将失效,失效缓存对应的内存空间将被释放。 所以,如果某张表中的数据变化频繁,而我们在查询这张表的内容时又使用了缓存,那么缓存失效的频率将会非常高,而且计算查询语句的hash值也是需要消耗资源的,当并发查询量大时,则需要考虑这些因素。综上所述,我们往往在数据变化不频繁、且又需要重复执行相同查询的场景中使用缓存。 那么,我们该怎样灵活的使用缓存呢,可以使用如下方法。 我们可以在开启缓存的时候(query_cache_type=ON),指定对应的查询语句不使用缓存,示例如下 <div class="enlighter-default enlighter-v-standard enlighter-t-enlighter enlighter-hover enlighter-overflow-scroll"> <div class="enlighter-toolbar"> <div class="enlighter-btn enlighter-btn-raw"></div> <div class="enlighter-btn enlighter-btn-copy"></div> <div class="enlighter-btn enlighter-btn-window"></div> </div> <div class="enlighter"> <div class=""> <div><span class="enlighter-text">select sql_no_cache name from stu;</span></div> </div> </div> </div> 我们也可以在按需使用缓存时(query_cache_type=DEMAND),指定对应的查询语句使用缓存,示例如下 <div class="enlighter-default enlighter-v-standard enlighter-t-enlighter enlighter-hover enlighter-overflow-scroll"> <div class="enlighter-toolbar"> <div class="enlighter-btn enlighter-btn-raw"></div> <div class="enlighter-btn enlighter-btn-copy"></div> <div class="enlighter-btn enlighter-btn-window"></div> </div> <div class="enlighter"> <div class=""> <div><span class="enlighter-text">select sql_cache name from stu;</span></div> </div> </div> </div> 上面两种使用方法感觉上好像是”缓存黑名单”与”缓存白名单”, 第一种是默认符合缓存条件的都缓存,只有使用sql_no_cache指定的语句不缓存。 第二种是默认所有查询语句的结果都不缓存,只有使用sql_cache指定的语句才会缓存。 个人觉得第二种比较好,明确指定需要缓存的语句,没有指明的都不缓存。 我们看几个例子,此处,我们在my.cnf中将查询缓存设置为按需缓存,同时,设置缓存空间大小为100M。 <img class=" lazyloaded" src="https://www.zsythink.net/wp-content/uploads/2017/01/011417_1131_2.png" alt="" data-src="https://www.zsythink.net/wp-content/uploads/2017/01/011417_1131_2.png" style=""> 在数据库中查看一下查询缓存相关的参数,可见,当前查询缓存为按需缓存。 当前设置最大的缓存结果为100M,单条最大缓存1M,也就是说当前设置最少能缓存100条查询结果。 <img class=" lazyloaded" src="https://www.zsythink.net/wp-content/uploads/2017/01/011417_1131_3.png" alt="" data-src="https://www.zsythink.net/wp-content/uploads/2017/01/011417_1131_3.png" style=""> 使用如下查询语句时,明确指定当前语句的查询结果需要缓存,我们重复执行3次如下语句。 <img class=" lazyloaded" src="https://www.zsythink.net/wp-content/uploads/2017/01/011417_1131_4.png" alt="" data-src="https://www.zsythink.net/wp-content/uploads/2017/01/011417_1131_4.png" style=""> 既然重复执行了3次上述语句,那么缓存结果应该是被命中过的,我们应该怎样查看,查询缓存的命中信息呢? 使用如下语句查看缓存命中情况 <div class="enlighter-default enlighter-v-standard enlighter-t-enlighter enlighter-hover enlighter-overflow-scroll"> <div class="enlighter-toolbar"> <div class="enlighter-btn enlighter-btn-raw"></div> <div class="enlighter-btn enlighter-btn-copy"></div> <div class="enlighter-btn enlighter-btn-window"></div> </div> <div class="enlighter"> <div class=""> <div><span class="enlighter-text">show status like </span><span class="enlighter-s0">'Qcache%'</span><span class="enlighter-text">;</span></div> </div> </div> </div> <img class=" lazyloaded" src="https://www.zsythink.net/wp-content/uploads/2017/01/011417_1131_5.png" alt="" data-src="https://www.zsythink.net/wp-content/uploads/2017/01/011417_1131_5.png" style=""> Qcache_queries_in_cache 表示已经缓存的SQL语句的数量,我们重复执行了3遍相同的查询语句,而且明确指定需要缓存,所以第一次执行就被缓存了。 Qcache_hits表示以被缓存的条目的命中次数,我们重复执行了3遍相同的查询语句,第一次被缓存,后两次被命中,所以此值为2。 Qcache_inserts表示在未命中缓存时,将查询结果写入缓存的次数,当我们第一次执行查询语句时,缓存中并没有对应缓存,所以第一次查询虽然未命中,但是会将查询结果写入缓存中一份。次值可以理解为未命中缓存,但是立即生成缓存的次数。 Qcache_lowmem_prunes 表示用于查询缓存的内存区域的修剪次数,什么意思呢?此处的修剪指的是当用于缓存的内存被沾满时,mysql会使用LRU算法清除命中率低的缓存项,从而空余出部分内存空间,用于缓存新的”查询缓存”,此值的数量越大,证明”修剪”的次数越多,如果短期内此值剧增,则表示我们分配的用于查询缓存的总大小偏小了,这时我们应该扩大缓存内存的值(query_cache_size的值)。 Qcache_not_cached 表示没有被缓存的查询语句的数量。 Qcache_free_memory表示查询缓存的空闲总量大小。 Qcache_free_blocks表示已分配的内存块中空闲块的数量。 Qcache_total_blocks表示当前查询缓存占用的内存的block数量。 我们可以通过上述统计信息的数值以及缓存设置相关的值,计算出查询缓存的相关指标,从而判断查询缓存是否对我们有一定的帮助,比如,通过上述数值计算出查询缓存的碎片率、缓存利用率、以及缓存命中率等信息。 <strong>查询缓存的碎片率</strong> =(Qcache_free_blocks / Qcache_total_blocks)* 100% <strong>查询缓存利用率</strong> = (query_cache_size – Qcache_free_memory) / query_cache_size * 100% 查询缓存的命中率的计算需要查询出另一个参数的值,我们稍后描述,先说说上面两个。 从上述两个公式我们可以推断出,如果Qcache_free_blocks的值过大,则碎片率越高,证明我们缓存内存碎片略多,可以尝试适当的调小query_cache_min_res_unit的值,也可以使用 FLUSH QUERY CACHE语句来清理缓存碎片。 如果查询缓存利用率太低,则表示query_cache_size设置的可能过大,可适度调小,如果缓存利用率非常高,同时Qcache_lowmem_prunes的值比较大,则表示query_cache_size的值设置的略小。 如果在调整query_cache_min_res_unit时不确定该调整为多大,可以使用如下公式计算出参考值。 query_cache_min_res_unit的预估值参考计算公式: (query_cache_size – Qcache_free_memory) / Qcache_queries_in_cache 如果想要计算查询缓存的命中率,可以使用如下公式(关于缓存命中率的计算存在争议,可以根据自己的理解与实际情况进行计算): <strong>查询缓存命中率</strong> = (Qcache_hits / Com_select)* 100% 上述公式中的Com_select表示查询语句的执行次数(这样描述并不准确),可以使用如下语句获得查询语句的执行次数 <div class="enlighter-default enlighter-v-standard enlighter-t-enlighter enlighter-hover enlighter-overflow-scroll"> <div class="enlighter-toolbar"> <div class="enlighter-btn enlighter-btn-raw"></div> <div class="enlighter-btn enlighter-btn-copy"></div> <div class="enlighter-btn enlighter-btn-window"></div> </div> <div class="enlighter"> <div class=""> <div><span class="enlighter-text">show status like </span><span class="enlighter-s0">"Com_select%"</span><span class="enlighter-text">;</span></div> </div> </div> </div> 如果命中率长期保持较低的状态,则证明查询缓存功能对我们的帮助不是很大,我们可以考虑调整相关参数或者是关闭查询缓存功能。 之前我们已经提到过,使用 flush query cache; 可以清理查询缓存碎片。 但是flush query cache;语句并不会从缓存中移出任何缓存,如果想要清除查询缓存中已经存在的缓存,可以使用如下语句 <div class="enlighter-default enlighter-v-standard enlighter-t-enlighter enlighter-hover enlighter-overflow-scroll"> <div class="enlighter-toolbar"> <div class="enlighter-btn enlighter-btn-raw"></div> <div class="enlighter-btn enlighter-btn-copy"></div> <div class="enlighter-btn enlighter-btn-window"></div> </div> <div class="enlighter"> <div class=""> <div><span class="enlighter-text">reset query cache;</span></div> </div> </div> </div> reset query cache;语句会从查询缓存中移除所有查询结果的缓存。 转载自朱双印日志https://www.zsythink.net/archives/1111 Last modification:May 29, 2024 © Allow specification reprint Like 如果觉得我的文章对你有用,请随意赞赏