MySQLQuery Cache保存查询返回的结果集。当查询命中缓存时,MySQL跳过解析、优化和执行阶段,直接返回查询结果。
Query Cache系统会跟踪查询中涉及的每一张表,如果这些表发生变化,那么和这个表相关的所有缓存数据都会失效。这种机制效率看起来比较低,因为在数据表变化时,查询到的结果可能没有发生变化。
QueryCache对应用程序是透明的,应用程序无需关心MySQL是通过Query Cache返回的还是实际执行SQL语句返回的。有一种方式查询缓存和原生的SQL工作方式有所不同,当要查询的表被LOCK TABLE(写锁)锁住时,查询仍然可以从Query Cache返回结果。通过参数query_cache_wlock_invalidate= OFF在锁定时刻仍然允许读取该表相关的 Query Cache,而query_cache_wlock_invalidate =ON 在写锁定的同时将使该表相关的所有 Query Cache 失效。
1. 判断缓存是否命中
(1) 缓存存放在一个引用表中,通过哈希值引用。通过一定的哈希规则生成唯一的key,select的结果生成value,即key-> value。该哈希值包含如下因素,即查询本身、当前要查询的数据库、客户端协议的版本等一些可能会影响结果的信息。
(2) 当判断是否命中时,不会解析、优化和执行SQL语句,而是直接使用SQL语句和客户端发送过来的其他原始信息。在SQL查询语句中,任何字符的不同,例如空格,注释,大小写等都会造成缓存不命中。
(3) 当查询语句中有一些不确定的数据,也不会被缓存。例如包含函数NOW()、CURRENT_DATE()、CURRENT_USER和CONNECTION_ID的查询不会被缓存。如果查询语句中包含任何不确定的函数,那么在QueryCache是不可能找到缓存结果。即时之前执行了这样的语句,但由于MySQL在任何时候发现不能被缓存的部分,就会禁止这个查询被缓存。
(4) Query Cache是在完整的SELECT语句基础上,而且只是在刚收到SQL语句的时候才检查,所以子查询和存储过程都没办法使用缓存。
(5) 打开查询缓存对读和写操作都会带来额外的消耗。
a. 读Query Cache之前必须先检查是否命中缓存。
b. 如果这个读查询可以被缓存,那么当完成执行后,MySQL若发现Query Cache没有这个查询,会将其结果存入Query Cache,带来额外的系统消耗。
c. 当写某个表写数据的时候,MySQL必须将表对应的所有缓存设置为失效。如果查询缓存非常大或者碎片很多,会带来很大的系统消耗。
2. Query Cache如何使用内存
当服务器启动的时候,需要初始化查询缓存所需要的内存。这个内存池是一个完整的空闲块。这个空闲块的大小是所配置查询缓存的大小减去维护元数据的数据结果所占用的空间。
mysql>show 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 |
+------------------------------+---------------------------+
query_cache_min_res_unit表示每次查询占用最小的空间大小。
query_cache_size设置 Query Cache 所使用的内存大小,默认值为0,大小必须是1024的整数倍。
当查询的结果需要缓存的时候,MySQL先从大的空间块中申请一个大于query_cache_min_res_unit的数据块,即使查询结果比这个小。因为需要在查询开始返回结果的时候就分配空间,而此时无法预知空间的大小。
因为需要先锁住空间块,然后找到合适大小的空闲数据块。相对来说,这是一个非常慢的操作,MySQL尽量避免这个操作次数。当需要缓存一个查询结果时,申请一个尽可能小的空闲内存块,将结果存入该内存块。如果该内存块已经存满了,此时还有数据需要存储,MySQL会重新申请分配一个空闲内存块,继续存储。当查询完成时,申请的内存块还有空闲空间,MySQL会释放该空闲空间。
服务器并发地向两个不同的连接返回结果,返回完结果后,MySQL回收剩余空闲空间时,该空闲空间大小小于query_cache_min_res_unit,因此该空间不能再次被Query Cache使用。由于并发返回结果,所以两个查询结果,MySQL会为两个结果同时分配空闲空间,而且查询2与查询1相邻。则在回收查询1所剩余的空闲空间时,由于小于query_cache_min_res_unit,所以查询1和查询2中间会产生碎片。