且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

MySQL管理之缓存机制

更新时间:2022-10-07 12:28:40

缓存机制简单的说就是缓存sql查询语句及查询结果,如果匹配到相同的查询,服务器则直接从缓存中取到结果。

缓存的数据之所以有用是因为数据没有发生过改变,如果改变原来的数据则缓存立即失效。

所以非常频繁读写小请求的场景,尤其是对innodb来讲 没有任何意义,关闭反而会提高性能

如果在此场景中如果有些场景查询操作非常大,缓存下来又非常有效:

mysql在提供缓存方面提供三个分隔:


1、开启 凡是能缓存则缓存

其中,涉及到用户私有信息,则不缓存,因此并不是所有场景都使用缓存的

·定义最大缓存数据

比如一次查询数据200M,但不能将这200M全部缓存至内存


2、如果不想使用缓存则可以OFF掉,将最大空间调整为0


3、按需分配 DEMAND

一旦定义为此类型并不是所有查询都被缓存进来,而是只有在查询的时候明确说明缓存的对象,它才会去缓存


以第一点为例,要实现内存的高效利用,一般要使用限制的,最小的对象不管数据有多大,则使用内存空间最小分配单元、以及最大缓存对象(以增强缓存有效利用率),在不同的场景中缓存的意义不尽相同,那么对于缓存来讲缓存对象和大小也不尽相同

而内存空间肯定是有限的,肯定不能将所有的数据都放入到内存里实现缓存效果

因此整体缓存大小都是事先定义好的

如果缓存起不到***效果,那么关闭是***解决方案

缓存是本地的,如果有多台mysql数据库,那么查询会被轮询到多台节点上,本地缓存命中率则低得多,那么则使用公共缓存提高命中率 比如memcache、redis等

但是由于memcache是在mysql之外的另台主机上,比起本地缓存性能要差,但由于是公共缓存又弥补了命中率低的场景,如果只是单台mysql则不用考虑公共缓存,用mysql自我管理缓存性能效果会更好


mysql缓存相关服务器变量

与缓存相关的服务器变量

mysql> showglobal variables like '%query_cache%';

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

|Variable_name                | Value    |

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

|have_query_cache             | YES      |

| query_cache_limit            | 1048576  |

|query_cache_min_res_unit     | 4096     |

|query_cache_size             | 16777216 |

|query_cache_type             | ON       |

|query_cache_wlock_invalidate | OFF      |

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

6 rows in set (0.00sec)

参数解释:

query_cache_size: 查询缓存的内存总大小,其必须是1024的整数倍,单位为字节。MySQL启动时,一次性分配并且初始化这里指定大小的内存空间。改变其值,MySQL会立刻删除所有的缓存对象并重新配置其大小及初始化。在性能较强的通用服务器上,查询缓存可能会成影响服务器扩展的因素,因为它存在成为服务器资源竞争单点的可能性,在多核心的服务器上甚至还有可能导致服务进程宕掉。

#这个值不能随意改变,每次改变都会导致mysql将所有缓存对象统统删除,并重新建立缓存的


query_cache_min_res_unit:存储缓存的最小内存块;这个值过小,会减少空间浪费,但会导致更频繁的内存块申请操作;设置的过大,会有着更高的碎片产生率。可以通过(query_cache_size-Qcache_free_memory)/Qcache_queryes_in_cache来获得一个接近理想的值。同时,如果Qcache_free_blocks存在空闲块,但Qcache_lowmem_prunes的值仍然在增长,则表明碎片过多导致了缓存结果会过早删除。

#查询缓存中空闲的内存空间(已经用了多少)

比如已经使用了10M空间,而这10M空间缓存了100个查询因此

10m 除以100 得出来的则是每个查询所占的空间,而这就是每个最小可分配的空间


查看与qure相关的统计值

mysql> showglobal status like '%qcache%';

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

|Variable_name           | Value    |

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

|Qcache_free_blocks      | 1        |

|Qcache_free_memory      | 16759696 |

| Qcache_hits             | 0        |

|Qcache_inserts          | 0        |

|Qcache_lowmem_prunes    | 0        |

| Qcache_not_cached       | 9        |

|Qcache_queries_in_cache | 0        |

|Qcache_total_blocks     | 1        |

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

8 rows in set (0.00sec)

参数解释:

query_cache_type: 是否打开查询缓存,其可用值有OFF、ON和DEMAND。DEMAND仅在查询语句中显式使用SQL_CACHE时才会使用缓存。

query_cache_limit: MySQL允许缓存的单个缓存对象的最大值。不过,MySQL只有在查询的所有结果都返回后才知道其是否超出此大小,但其在查询一开始便会尝试使用缓存存储查询结果,一旦发现超时可缓存最大值则会从缓存中将其删除,并增大Qcache_not_cached的值。因此,如果知道某查询的结果会超出可缓存的最大对象,则应该在查询语句中使用SQL_NO_CACHE。

query_cache_wlock_invalidate:如果某个数据表被其它的连接锁住,是否仍然从查询缓存中返回结果。

OFF表示返回。

#当查询的时候,所关系的表被事物锁住了,有可能事物会更改里面的值,那我们仍然会从缓存中缓存结果很肯定会有错误,那么这里意思为将表锁住的时候是否还允许在缓存中返回数据,OFF为允许 ON反而不允许,一般不用关心


返回状态变量

mysql>  show global status like '%qcache%';

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

|Variable_name           | Value    |

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

|Qcache_free_blocks      | 1        |

|Qcache_free_memory      | 16759696 |

| Qcache_hits             | 0        |

|Qcache_inserts          | 0        |

|Qcache_lowmem_prunes    | 0        |

| Qcache_not_cached       | 9        |

|Qcache_queries_in_cache | 0        |

|Qcache_total_blocks     | 1        |

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

8 rows in set (0.00sec)


参数解释:

Qcache_free_blocks :在内存中已经分配出去的块,但块却没有被使用存储缓存对象的

Qcache_free_memory :没有被划分为块的

Qcache_hits:命中次数

Qcache_inserts :插入缓存对象

#既然是缓存那么肯定是缓存对象,既然建立了一次缓存对象,就表示向缓存中插入一次数据(缓存对象)


Qcache_not_cached : 查询语句没有被缓存的个数

Qcache_queries_in_cache: 保存在缓存中的查询个数

Qcache_total_blocks : 已经划分好的块数


mysql如何查询缓存

只要启动了缓存功能,处理任何一个查询语句都会先去查找缓存


mysql的缓存工作流程

mysql 在建立缓存的时候都会使用键值缓存的工作机制

每个缓存都会有一个"键"(key) 缓存的结果是值(value)

将查询语句当做键(key);

将返回结果当做值(value);


当一个查询语句请求到来的时候查询键里是否存在这个语句,如果存在则命中,反之。

大部分键都是以哈希方式进行对比的,哈希本身是区分字符大小写,所以如果启动缓存以后如果想让缓存命中率提高,必须养成习惯:

统一使用分割,要么全部大写,要么全部小写,不管谁写的代码查询风格(字母大小写)必须统一

缓存键的生成不包括语句的本身,还包括生成的时间,还包括mysql数据库的协议版本等

它将众多元素柔和起来生成键,所以如果使用不同的客户端去查询可能都无法命中

因此建议使用相同版本的mysql 客户端接口等


不确定性的内容

以下信息都为敏感信息都不缓存的:

·用户自动函数

·用户自定义变量

·临时表

·mysql库中的系统表

·列级别(某个查询,用户都在列级别下都不会被缓存)

·存数函数

·不确定数据


判断这些瓶颈,可以在服务器级别将抓包将查询语句都抓出来并对其分析使用风格是否统一,再通过这些变量来分析缓存是否发生作用了,如果很低,则不建议使用缓存。


命中率的计算

公式:hits rate = Qcache_hists/(Qcache_hits+Com_select)

#命中的次数除以 (命中的次数 + select语句的个数【只要没命中 都会增加这个值的,如果命中则增加的是Qcache_hits】) = 大致命中率

下面以我们单位一台测试机为例

mysql> showglobal status like '%qcache%';

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

|Variable_name           | Value     |

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

|Qcache_free_blocks      | 692       |

|Qcache_free_memory      | 518259760 |

| Qcache_hits             | 232464273 |

|Qcache_inserts          | 30710570  |

|Qcache_lowmem_prunes    | 0         |

| Qcache_not_cached       | 153693403 |

|Qcache_queries_in_cache | 17670     |

|Qcache_total_blocks     | 36043     |

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

8 rows in set (0.00sec)

查看com_select

mysql> showsession status like 'com_select';

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

| Variable_name |Value |

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

| Com_select    | 3    |

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

1 row in set (0.00sec)


232464273    /      (232464273 + 1+3 ) 约等于 0.9


一般来讲命中率的结果能在30%以上就说明缓存能够带来帮助,此外还要关注另外的指标:

“命中和写入”的比率:即Qcache_hits和Qcache_inserts的比值

此比值大于3:1时通常查询缓存是有效的,能达到甚至大于10:1就更好了


如果我们发现缓存不太有效,那么则关闭缓存

如果我们发现因此碎片才导致命中率低,那么则可以使用碎片整理


mysql> flushquery cache ;

Query OK, 0 rowsaffected (0.57 sec)


但是整理的过程可能导致缓存无法使用,因为mysql任何查询都需要先查缓存,而缓存无法用,所以可能会导致阻塞,类似于卡一段时间,查询很大的话可能会等待很久,如果访问非常频繁的话那么后果会非常严重


清理缓存

reset query cahche

Query OK, 0 rowsaffected (0.57 sec)


有必要的时候,尤其是对于非常复杂的统计操作,就算是命中率低也要启动缓存,只是我们要启动DEMAND方式,明确指定只缓存的类型



通用缓存优化思路
1
、批量写入而非单个写入。批量写入仅一次性影响缓存。

因为一次插入一行,和每次插入操作都会导致缓存失效一次,而批量插入则使一批失效


2、过大的缓存空间,会使得在大量缓存对象过期失效时导致服务器假死。

缓存无论如何不要太大


3、必要时,使用SQL_CACHE和SQL_NO_CACHE手动控制缓存动作。


4、对写密集型的场景来说,禁用缓存可以提高性能。

失效太多,意义则不大了,因此则禁用缓存则可以提高性能,也就是之前一直强调的



本文转自zuzhou 51CTO博客,原文链接:http://blog.51cto.com/yijiu/1392483