MySQL的查询缓存

MySQL的查询缓存

微信搜索 zze_coding 或扫描 👉 二维码关注我的微信公众号获取更多资源推送:

查询缓存组件

MySQL 在服务器层维护了一个查询缓存的组件,如下图:
MySQL的查询执行流程
这个查询缓存组件缓存的是完整 select 语句的查询结果,并且缓存数据是完全被维护在内存中的。
因此,在相同 select 语句到达 MySQL 服务器时,MySQL 可以通过对 select 语句进行 hash 得到对应 key,然后从查询缓存直接获得并返回结果,就跳过了解析、优化、执行等阶段,从而大大减少了整个查询所需要的时长。
so... 查询缓存组件是个灰常灰常重要的组件~~

MySQL 通过查询语句的哈希值判断是否可以命中查询缓存,哈希值考虑的因素可能还包括查询语句本身、要查询的数据库、客户端使用的协议版本等。。。
这里有一点要注意,两条相同功用的查询语句即便仅有大小写的不同,它们也不能命中同一个查询缓存,因为它们的哈希值是不同的,所以这里要注意统一编写 SQL 的风格~

事实上,查询缓存组件会跟踪查询缓存中涉及到的每一张表,如果对应表发生了变化那么该查询缓存将会立即失效,以维护查询缓存中缓存条目的有效性。
所以如果一张表经常被修改的话,针对这张表的查询缓存可能还没有被命中就被清除失效了,因此更新灰常频繁的表是不适合应用查询缓存的。

不过,随着现在通用服务器越来越大,查询缓存也被发现是可能影响服务器扩展的一个因素,为啥呢?
因为现在的物理服务器的核心数越来越多了,每一个核心上都可以同时单独执行一个 SQL 语句,所以单台服务器同时能执行 SQL 语句也越来越多了,这些语句在执行之前都得先查询缓存是否可以命中,所以如果服务器有 32 或 64 个物理核心,结果可想而知~~所以查询缓存反倒有可能成为整个服务器多个核心竞争资源的热点及性能瓶颈了。。
所以在一个性能及其强大的服务器上,查询缓存的大小不是越大越好(大则使用查询缓存的几率高),通常几十 M 就足矣。

除此之外使用查询缓存组件的确能够大大提升查询效率,并且查询缓存组件对应用程序或终端用户来讲是完全透明的,用户根本无需关心 MySQL 是通过查询缓存还是解析、优化、执行以后从磁盘中获取的结果。

不可被缓存的查询

并不是所有查询都能被缓存,下面列举几个不会缓存的场景:

  • 如果查询语句中包含了一些不确定的数据时,这个查询就不能被缓存,比如查询语句中包含了 now()current_date() 等等;
  • 如果查询包含了任何用户自定义的函数、用户自定义变量、临时表、mysql 库中的系统表等等,该查询就不会被缓存;
  • 如果查询任何包含了列级别权限的表时,该查询也不会被缓存,因为列级别权限过于精细,可能导致不同的用户查询的结果也不相同;

查询缓存相关变量

几个查看查询缓存相关变量的命令:

show global variables like 'query%';    -- 查看查询缓存设置
show global status like 'Qcache%';      -- 查询相关的状态变量
show global status like 'Com_se%';      -- 实际查询总次数
show global variables like 'have_query_cache'; -- 查看查询缓存是否可用

-- 缓存命中率的评估:Qcache_hits/(Qcache_hits+Com_select)

服务器变量

可通过如下语句来查看查询缓存相关的服务器变量:

mysql> show variables like '%query%';
+------------------------------+--------------------------------------+
| Variable_name                | Value                                |
+------------------------------+--------------------------------------+
| binlog_rows_query_log_events | OFF                                  |
| ft_query_expansion_limit     | 20                                   |
| have_query_cache             | YES                                  |
| long_query_time              | 10.000000                            |
| query_alloc_block_size       | 8192                                 |
| query_cache_limit            | 1048576                              |
| query_cache_min_res_unit     | 4096                                 |
| query_cache_size             | 1048576                              |
| query_cache_type             | OFF                                  |
| query_cache_wlock_invalidate | OFF                                  |
| query_prealloc_size          | 8192                                 |
| slow_query_log               | OFF                                  |
| slow_query_log_file          | /data/3306/data/centos7-200-slow.log |
+------------------------------+--------------------------------------+
13 rows in set (0.00 sec)

其中主要的几个变量说明如下:

  • query_cache_min_res_unit:查看缓存中内存块的最小分配单位,即结果集大小小于该大小的查询不缓存,较小值会减少浪费,但会导致更频繁的内存分配操作,较大值会带来浪费,导致碎片过多;
  • query_cache_limit:能够缓存的最大查询结果,对于有着较大结果的查询语句,建议在 select 中使用 sql_no_cache 显式指定不使用缓存;
  • query_cache_size:查询缓存总共可用的内存空间,单位是字节,必须是 1024 的整数倍;
  • query_cache_type:可选值有 ONOFFDEMANDON 表示尽量缓存,OFF 表示不缓存,DEMAIN,显示指定缓存才缓存;
  • query_cache_wlock_invalidate:如果某表被其它的连接锁定,是否仍然可以从查询缓存中返回结果,默认值为 OFF,表示可以在表被其它连接锁定的场景中继续从缓存中返回数据,ON 则表示不允许;

状态变量

可通过如下来查看查询相关的状态变量:

mysql> show status like 'Qcache%';
+-------------------------+---------+
| Variable_name           | Value   |
+-------------------------+---------+
| Qcache_free_blocks      | 1       |
| Qcache_free_memory      | 1031832 |
| Qcache_hits             | 0       |
| Qcache_inserts          | 0       |
| Qcache_lowmem_prunes    | 0       |
| Qcache_not_cached       | 26021   |
| Qcache_queries_in_cache | 0       |
| Qcache_total_blocks     | 1       |
+-------------------------+---------+
8 rows in set (0.01 sec)

其中各项变量说明如下:

  • Qcache_free_blocks:空闲的内存块个数;
  • Qcache_free_memory:空闲的内存空间;
  • Qcache_hits:缓存命中次数;
  • Qcache_inserts:可缓存查询语句被放入查询缓存的次数;
  • Qcache_lowmem_prunes:因查询缓存空间过小而通过 LRU 清理缓存的次数;
  • Qcache_not_cached:查询语句可缓存但没有被缓存的次数;
  • Qcache_queries_in_cache:在当前查询缓存空间中被缓存下来的查询的个数;
  • Qcache_total_blocks:整个查询缓存一共的内存块;

判断查询缓存是否高效

MySQL缓存参数调整
小结:

  • 如果大量可缓存的语句结果没有被缓存是因为查询缓存结果过大导致的,此时需要增大 query_cache_limit 的值;
  • 如果缓存的命中率不高是因为内存碎片化过于严重导致的,此时需要减小 query_cache_min_res_unit 的值;
  • 如果大量缓存频繁失效是因为缓存空间过小导致的,此时需要增大 query_cache_size 的值;

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://www.zze.xyz/archives/mysql-sql-cache.html

Buy me a cup of coffee ☕.