mysql 缓存机制

浪尽此生 提交于 2019-12-11 08:49:43

MySQL8.0已经取消了查询缓存。如果是小型项目,又不想用Reids或者MC等缓存,查询结果相对固定,可以在MySQL5.7以下版本设置缓存。


执行逻辑:mysql缓存机制就是缓存sql 文本缓存结果,用key value 的形式存储在服务器内存中;
如果运行相同的sql,服务器直接从缓存中去获取结果,不需要在再去解析、优化、执行sql。

缓存失效:如果这个表修改了,那么这个表中的所有缓存将不再有效,查询缓存 的值 的相关条目将被清空。
表中得任何改变是指表中任何数据或者是结构的改变,包括insert,update,delete,truncate,alter table,drop table或者是drop database 包括那些映射到改变了的表的使用merge表的查询;


Query Cache的工作原理、如何配置、如何维护、如何判断查询缓存的性能、适合的业务场景分析。

工作原理

查询缓存的工作原理,基本上可以用二句话概括:

缓存了:SELECT操作或预处理查询(注释:5.1.17开始支持)的结果集和SQL语句;

新的SELECT语句或预处理查询语句,先去查询缓存,判断是否存在可用的记录集,判断标准:与缓存的SQL语句,是否完全一样,区分大小写;


查询缓存对什么样的查询语句,无法缓存其记录集,大致有以下几类:

  • 查询语句中加了SQL_NO_CACHE参数;
  • 查询语句中含有获得值的函数,包涵自定义函数,如:CURDATE()、GET_LOCK()、RAND()、CONVERT_TZ等;
  • 对系统数据库的查询:mysql、information_schema
  • 查询语句中使用SESSION级别变量或存储过程中的局部变量;
  • 查询语句中使用了LOCK IN SHARE MODE、FOR UPDATE的语句
  • 查询语句中类似SELECT …INTO 导出数据的语句;
  • 事务隔离级别为:Serializable情况下,所有查询语句都不能缓存;
  • 对临时表的查询操作;
  • 存在警告信息的查询语句;
  • 不涉及任何表或视图的查询语句;
  • 某用户只有列级别权限的查询语句;

查询缓存的优缺点:

优点:

  1. 对于一些不变的数据且有大量相同sql查询的表,查询缓存会节省很大的性能;
  2. 不需要对SQL语句做任何解析和执行,当然语法解析必须通过在先,直接从Query Cache中获得查询结果;

缺点:

  1. 对于更新频繁的表,查询缓存不合适;
  2. 查询缓存的判断规则,不够智能,也即提高了查询缓存的使用门槛,降低其效率;
  3. Query Cache的起用,会增加检查和清理Query Cache中记录集的开销,而且存在SQL语句缓存的表,每一张表都只有一个对应的全局锁;

命中条件

缓存存在一个hash表中,通过查询SQL,查询数据库,客户端协议等作为key,在判断命中前,mysql不会解析SQL,而是使用SQL去查询缓存;
SQL上的任何字符的不同,如空格,注释,都会导致缓存不命中。
如果查询有不确定的数据 like 、now()、current_date(),那么查询完成后结果者不会被缓存,包含不确定的数的是不会放置到缓存中。

工作流程

1.服务器接收SQL,以SQL和一些其他条件为key查找缓存表
2.如果找到了缓存,则直接返回缓存
3.如果没有找到缓存,则执行SQL查询,包括原来的SQL解析,优化等。
4.执行完SQL查询结果以后,将SQL查询结果缓存入缓存表

缓存失败

当某个表正在写入数据,则这个表的缓存(命中缓存,缓存写入等)将会处于失效状态,在Innodb中,如果某个事务修改了这张表,则这个表的缓存在事务提交前都会处于失效状态,在这个事务提交前,这个表的相关查询都无法被缓存。

缓存的内存管理

缓存会在内存中开辟一块内存(query_cache_size)来维护缓存数据,其中大概有40K的空间是用来维护缓存数据的元数据的,例如空间内存,数据表和查询结果映射,SQL和查询结果映射的。
mysql将这个大内存块分为小内存块(query_cache_min_res_unit),每个小块中存储自身的类型、大小和查询结果数据,还有前后内存块的指针。
mysql需要设置单个小存储块大小,在SQL查询开始(还未得到结果)时就去申请一块内存空间,所以即使你的缓存数据没有达到这个大小也需要这个大小的数据块去保存(like linux filesystem’s block)。如果超出这个内存块的大小,则需要再申请一个内存块。当查询完成发现申请的内存有富余,则会将富余的内存空间是放点,这就会造成内存碎片的问题,见下图

è¿éåå¾çæè¿°

缓存的使用时机

衡量打开缓存是否对系统有性能提升是一个很难的话题

  1. 通过缓存命中率判断, 缓存命中率 = 缓存命中次数 (Qcache_hits) / 查询次数 (Com_select)
  2. 通过缓存写入率, 写入率 = 缓存写入次数 (Qcache_inserts) / 查询次数 (Qcache_inserts)
  3. 通过 命中-写入率 判断, 比率 = 命中次数 (Qcache_hits) / 写入次数 (Qcache_inserts), 高性能MySQL中称之为比较能反映性能提升的指数,一般来说达到3:1则算是查询缓存有效,而最好能够达到10:1

缓存参数配置

1、是否启用mysql查询缓存:

可以通过2个参数:query_cache_type和query_cache_size,
其中任何一个参数设置为0都意味着关闭查询缓存功能,但是正确的设置推荐query_cache_type=0。

query_cache_type

  • 值域为:0 -– 不启用查询缓存;
  • 值域为:1 -– 启用查询缓存,只要符合查询缓存的要求,客户端的查询语句和记录集斗可以缓存起来,共其他客户端使用;
  • 值域为:2 -– 启用查询缓存,只要查询语句中添加了参数:sql_cache,且符合查询缓存的要求,客户端的查询语句和记录集,则可以缓存起来,共其他客户端使用;

query_cache_size

允许设置query_cache_size的值最小为40K,对于最大值则可以几乎认为无限制,实际生产环境的应用经验告诉我们,并不是该值越大, 查询缓存的命中率就越高,也不是对服务器负载下降贡献大,反而可能抵消其带来的好处,甚至增加服务器的负载,至于该如何设置,下面的章节讲述,推荐设置 为:64M;

query_cache_limit

限制查询缓存区最大能缓存的查询记录集,可以避免一个大的查询记录集占去大量的内存区域,而且往往小查询记录集是最有效的缓存记录集,默认设置为1M,建议修改为16k~1024k之间的值域,不过最重要的是根据自己应用的实际情况进行分析、预估来设置;

query_cache_min_res_unit

设置查询缓存分配内存的最小单位,要适当地设置此参数,可以做到为减少内存块的申请和分配次数,但是设置过大可能导致内存碎片数值上升。默认值为4K,建议设置为1k~16K

query_cache_wlock_invalidate

该参数主要涉及MyISAM引擎,若一个客户端对某表加了写锁,其他客户端发起的查询请求,且查询语句有对应的查询缓存记录,是否允许直接读取查询缓存的记录集信息,还是等待写锁的释放。默认设置为0,也即允许;

性能监控

碎片率
查询缓存内存碎片率=Qcache_free_blocks / Qcache_total_blocks * 100%

命中率
查询缓存命中率=(Qcache_hits – Qcache_inserts) / Qcache_hits * 100%

内存使用率
查询缓存内存使用率=(query_cache_size – Qcache_free_memory) / query_cache_size * 100%

Qcache_lowmem_prunes

该参数值对于检测查询缓存区的内存大小设置是否,有非常关键性的作用,其代表的意义为:查询缓存去因内存不足而不得不从查询缓存区删除的查询缓存信息,删除算法为LRU;

query_cache_min_res_unit

内存块分配的最小单元非常重要,设置过大可能增加内存碎片的概率发生,太小又可能增加内存分配的消耗,为此在系统平稳运行一个阶段性后,可参考公式的计算值:

查询缓存最小内存块 = (query_cache_size – Qcache_free_memory) / Qcache_queries_in_cache

query_cache_size

我们如何判断query_cache_size是否设置过小,依然也只有先预设置一个值,推荐为:32M~128M之间的区域,待系统平稳运行一个时间段(至少1周),并且观察这周内的相关状态值:

(1). Qcache_lowmem_prunes;

(2). 命中率;

(3). 内存使用率;

若整个平稳运行期监控获得的信息,为命中率高于80%,内存使用率超过80%,并且Qcache_lowmem_prunes的值不停地增加,而且增加的数值还较大,则说明我们为查询缓冲区分配的内存过小,可以适当地增加查询缓存区的内存大小;

若是整个平稳运行期监控获得的信息,为命中率低于40%,Qcache_lowmem_prunes的值也保持一个平稳状态,则说明我们的查询缓冲区的内 存设置过大,或者说业务场景重复执行一样查询语句的概率低,同时若还监测到一定量的freeing items,那么必须考虑把查询缓存的内存条小,甚至关闭查询缓存功能;

业务场景

通过上述的知识梳理和分析,我们至少知道查询缓存的以下几点:

查询缓存能够加速已经存在缓存的查询语句的速度,可以不用重新解析和执行而获得正确得记录集;

查询缓存中涉及的表,每一个表对象都有一个属于自己的全局性质的锁;

表若是做DDL、FLUSH TABLES 等类似操作,触发相关表的查询缓存信息清空;

表对象的DML操作,必须优先判断是否需要清理相关查询缓存的记录信息,将不可避免地出现锁等待事件;

查询缓存的内存分配问题,不可避免地产生一些内存碎片;

查询缓存对是否是一样的查询语句,要求非常苛刻,而且还不智能;

我们再重新回到本节的重点上,查询缓存适合什么样的业务场景呢?只要是清楚了查询缓存的上述优缺点,就不难罗列出来,业务场景要求:

整个系统以读为主的业务,比如门户型、新闻类、报表型、论坛等网站;

查询语句操作的表对象,非频繁地进行DML操作,可以使用query_cache_type=2模式,然后SQL语句加SQL_CACHE参数指定;

 

GLOBAL STAUS 中 关于 缓存的参数解释:

Qcache_free_blocks: 缓存池中空闲块的个数
Qcache_free_memory: 缓存中空闲内存量
Qcache_hits: 缓存命中次数
Qcache_inserts: 缓存写入次数
Qcache_lowmen_prunes: 因内存不足删除缓存次数
Qcache_not_cached: 查询未被缓存次数,例如查询结果超出缓存块大小,查询中包含可变函数等
Qcache_queries_in_cache: 当前缓存中缓存的SQL数量
Qcache_total_blocks: 缓存总block数

 

维护(减少碎片策略)

查询缓存区的碎片整理

首先:最开始时,应选择合适的block大小;
       其次:查询缓存使用一段时间之后,一般都会出现内存碎片,为此需要监控相关状态值,并且定期进行内存碎片的整理;
       碎片整理的操作语句:FLUSH QUERY CACHE(使用此命令整理缓存期间,会导致其他连接无法使用查询缓存);

全部清空查询缓存的数据的命令:
       (1). RESET QUERY CACHE;
       (2). FLUSH TABLES;

 

InnoDB与查询缓存

Innodb会对每个表设置一个事务计数器,里面存储当前最大的事务ID.当一个事务提交时,InnoDB会使用MVCC中系统事务ID最大的事务ID跟新当前表的计数器.
只有比这个最大ID大的事务能使用查询缓存,其他比这个ID小的事务则不能使用查询缓存.
另外,在InnoDB中,所有有加锁操作的事务都不使用任何查询缓存
查询必须是完全相同的(逐字节相同)才能够被认为是相同的。另外,同样的查询字符串由于其它原因可能认为是不同的。使用不同的数据库、不同的协议版本或者不同 默认字符集的查询被认为是不同的查询并且分别进行缓存。

 

调优:

调优手段
前面提到每次查询都会先检查缓存。如果需要缓存的查询很少,可以将query_cache_type设置为demand,然后对需要缓存的查询后面添加sql_cache。这样可以更细粒度的控制缓存的使用
如果写入操作很多(前面提到修改表的时候会使缓存失效),这个时候可以禁用缓存,并将缓存大小query_cache_size设置为0,这样就不会占用内存。
基于上一条,如果两者都要,那么就让写操作成批量的执行,而不是逐个执行。

 调优参数

query_cache_type:既是会话级变量也是全局性变量。有on/off/demand三个属性
query_min_cache_res_unit:设置分配缓存最小块大小。
query_cache_limit:限制缓存存储的最大结果,超过不会被缓存


参考:https://blog.csdn.net/jjxojm/article/details/89741765

1.  在my.cnf中添加如下配置并重启,开启MySQL缓存

query_cache_size=256M
query_cache_limit=10M
query_cache_type=1
 query_cache_size为总缓存大小,官方推荐不要超过256M
 query_cache_limit为单条查询结果的最大缓存大小,如不设置,默认为1M
 query_cache_type表示缓存方式,0表示不开启缓存,1表示每条语句都开启缓存(除非指定不需要缓存),2表示如果查询语句中指定使用缓存,则进行缓存,具体见后续说明。
2.  开启完毕后,进入MySQL命令行,输入如下命令确认各参数是否设置正确:

show variables like '%query_cache%';
3.  可用如下命令确认缓存状态:

show status like 'Qcache%';
4.  重置缓存内容:

reset query cache;
除了上述简单开启外,额外说明如下:

开启缓存后,MySQL会根据查询语句与关联表进行查询结果的缓存,一旦关联表发生变化,就会使缓存内容失效;
虽然没有试验,但据说开启缓存应该是对相关表的插入更新效率是有一定影响的;
如前所述,query_cache_type=2时,只缓存明确需要缓存的SQL查询(在SELECT后加上SQL_CACHE),这样可以避免滥用缓存, 根据实际需要进行优化;当query_cache_type=1时,也可以通过SQL_NO_CACHE来关闭查询缓存。具体方式如下:
SELECT SQL_CACHE ... FROM ... WHERE ...
SELECT SQL_NO_CACHE ... FROM ... WHERE ...
       总的说来,在部分变化频率较低的表对应的查询语句中,开启Query Cache,对性能会有一定提升,其余情况下,个人感觉还是尽量少用,遇到问题再优化。


其他参考:https://www.cnblogs.com/hadley/p/9557596.html

其他参考:https://blog.csdn.net/luyaran/article/details/81066794

其他参考:https://blog.csdn.net/qq_41949579/article/details/80511839

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!