查询缓存是
MySQLMySQL中比较独特的一个缓存区域 , 用来缓存特定Query的整个结果集信息 , 且共享给所有客户端。为了提高完全相同的Query语句的响应速度 , MySQL Server会对查询语句进行Hash计算后 , 把得到的hash值与Query查询的结果集对应存放在Query Cache中。当MySQL Server打开Query Cache之后 , MySQL Server 会对接收到的每一个SELECT 语句通过特定的Hash算法计算该Query的Hash值 , 然后通过该hash值到Query Cache中去匹配。
如果没有匹配 , 将这个
hash值存放在一个hash链表中 , 并将Query的结果集存放到cache中 , 存放hash值链表的每个hash节点存放了相应Quey结果集在cache中的地址 , 以及该query所涉及到一些table相关信息;如果通过
hash值匹配到了一样的Query,则直接将cache中相应的Query结果集返回给客户端。
1. 查询缓存原理
查询执行路径

查询缓存原理
MySQL Query Cache是用来缓存我们所执行的SELECT语句以及该语句的结果集 , MySql在实现Query Cache的具体技术细节上类似典型的KV存储 , 就是将SELECT语句和该查询语句的结果集做了一个HASH映射并保存在一定的内存区域中。当客户端发起SQL查询时 , Query Cache的查找逻辑是 , 先对SQL进行相应的权限验证 , 接着就通过Query Cache来查找结果(注意必须是完全相同 , 即使多一个空格或者大小写不同都认为不同 , 即使完全相同的SQL , 如果使用不同的字符集、不同的协议等也会被认为是不同的查询而分别进行缓存)。它不需要经过Optimizer 模块进行执行计划的分析优化 , 更不需要发生同任何存储引擎的交互 , 减少了大量的磁盘IO和CPU运算 , 所以有时候效率非常高。
缓存SELECT操作或预处理查询的结果集和SQL语句 , 当有新的SELECT语句或预处理查询语句请求 , 先去查询缓存 ,判断是否存在可用的记录集 , 判断标准: 与缓存的SQL语句 , 是否完全一样 , 区分大小写。
优缺点
不需要对SQL语句做任何解析和执行 , 当然语法解析必须通过在先 , 直接从Query Cache中获得查询结果 , 提高查询性能查询缓存的判断规则 , 不够智能 , 也即提高了查询缓存的使用门槛 , 降低效率查询缓存的使用 , 会增加检查和清理Query Cache中记录集的开销。
哪些查询可能不会被缓存
查询语句中加了
SQL_NO_CACHE参数查询语句中含有获得值的函数 , 包含: 自定义函数 , 如 :
NOW()、CURDATE()、GET_LOCK()、RAND()、CONVERT_TZ()等对系统数据库的查询 :
mysql、information_schema查询语句中使用
SESSION级别变量或存储过程中的局部变量查询语句中使用了
LOCK IN SHARE MODE、FOR UPDATE的语句 , 查询语句中类似SELECT … INTO导出数据的语句对临时表的查询操作
存在警告信息的查询语句
不涉及任何表或视图的查询语句
某用户只有列级别权限的查询语句
事务隔离级别为
Serializable时,所有查询语句都不能缓存
MySQL8.0 取消查询缓存的功能
尽管MySQL Query Cache旨在提高性能 , 但它存在严重的可伸缩性问题 , 并且很容易成为严重的瓶颈。 自MySQL 5.6(2013)以来 , 默认情况下已禁用查询缓存 , 其不能与多核计算机上在高吞吐量工作负载情况下进行扩展。 另外有时因为查询缓存往往弊大于利。比如 : 查询缓存的失效非常频繁 , 只要有对一个表的更新 , 这个表 上的所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来 , 还没使用呢 , 就被一个更新全清空了。对于更新压力大的数据库来说 , 查询缓存的命中率会非常低。除非你的业务有一张静态表 , 很长时间更新一次 , 比如系统配置表 , 那么这张表的查询才适合做查询缓存。目前大多数应用都把缓存做到了应用逻辑层 , 比如 : 使用redis或者memcache。
2. 查询缓存相关服务器变量
query_cache_min_res_unit: 查询缓存中内存块的最小分配单位 , 默认4k。该值较小会减少浪费 , 但会导致更频繁的内存分配操作
该值较大会带来浪费,会导致碎片过多,内存不足
query_cache_limit: 单个查询结果能缓存的最大值容量 , 单位字节 , 默认为1M。对于查询结果过大而无法缓存的语句 , 建议使用SQL_NO_CACHE。query_cache_size: 查询缓存总共可用的内存空间。缺省情况下 , 查询缓存是关闭的。单位字节 , 必须是1024的整数倍 , 最小值40KB, 低于此值有警报 。query_cache_wlock_invalidate: 如果某表被其它的会话锁定 , 是否仍然可以从查询缓存中返回结果 ,默认值为OFF, 表示可以在表被其它会话锁定的场景中继续从缓存返回数据 ;ON则表示不允许query_cache_type: 是否开启缓存功能 . 取值为ON,OFF,DEMAND。0(OFF): 关闭Query Cache功能 , 任何情况下都不会使用Query Cache;1(ON): 开启Query Cache功能 , 查询缓存功能打开 ,SELECT的结果符合缓存条件即会缓存 , 否则 , 不予缓存 。但是当SELECT语句中使用SQL_NO_CACHE提示后 , 将不使用QueryCache;2(DEMAND): 开启Query Cache功能 , 查询缓存功能按需进行 , 显式指定SQL_CACHE的SELECT语句才会缓存 ; 其它均不予缓存
如果
query_cache_type为on而又不想利用查询缓存中的数据 , 可以用下面的SQL:SELECT SQL_NO_CACHE * FROM my_table WHERE condition;如果值为
2, 要使用缓存的话 , 需要使用SQL_CACHE开关参数 :SELECT SQL_CACHE * FROM my_table WHERE condition;
3. 查询缓存相关的状态变量
SHOW GLOBAL STATUS LIKE 'Qcache%';
MariaDB [(none)]> SHOW GLOBAL STATUS LIKE 'Qcache%';
+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 1031320 |
| Qcache_hits | 0 |
| Qcache_inserts | 0 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 0 |
| Qcache_queries_in_cache | 0 |
| Qcache_total_blocks | 1 |
+-------------------------+---------+
8 rows in set (0.002 sec)
Qcache_free_blocks: 处于空闲状态Query Cache中内存Block数Qcache_total_blocks:Query Cache中总Block, 当Qcache_free_blocks相对此值较大时 , 可能用内存碎片, 执行FLUSH QUERY CACHE清理碎片Qcache_free_memory: 处于空闲状态的Query Cache内存总量Qcache_hits:Query Cache命中次数Qcache_inserts: 向Query Cache中插入新的Query Cache的次数 , 即没有命中的次数Qcache_lowmem_prunes: 记录因为内存不足而被移除出查询缓存的查询数Qcache_not_cached: 没有被Cache的SQL数 , 包括无法被Cache的SQL以及由于query_cache_type设置的不会被Cache的SQL语句Qcache_queries_in_cache: 在Query Cache中的SQL数量
4. 查询的优化

5. 命中率和内存使用率估算
查询缓存中内存块的最小分配单位
query_cache_min_res_unit:(query_cache_size - Qcache_free_memory) / Qcache_queries_in_cache查询缓存命中率
Qcache_hits / ( Qcache_hits + Qcache_inserts ) * 100%查询缓存内存使用率:
(query_cache_size – qcache_free_memory) / query_cache_size * 100%
[root@centos7 ~]#vim /etc/my.cnf
[mysqld]
query_cache_type=ON
query_cache_size=10M
[root@centos8 ~]#systemctl restart mariadb
[root@centos8 ~]#mysql -uroot -p
MariaDB [(none)]> show variables like 'query_cache%';
+------------------------------+---------+
| Variable_name | Value |
+------------------------------+---------+
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 1048576 |
| query_cache_strip_comments | OFF |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
+------------------------------+---------+
6 rows in set (0.001 sec)
MariaDB [(none)]> SHOW GLOBAL STATUS LIKE 'Qcache%';
+-------------------------+---------+
| Variable_name | Value |
+-------------------------+---------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 1027736 |
| Qcache_hits | 3 |
| Qcache_inserts | 3 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 0 |
| Qcache_queries_in_cache | 3 |
| Qcache_total_blocks | 8 |
+-------------------------+---------+
8 rows in set (0.003 sec)