Query Cache在其他数据库里面也称为结果集缓存。顾名思义,它的目的是将SELECT语句与其返回结果缓存到Query Cache中,如果重复执行相同的SELECT语句的话,我们可以跳过MySQL的解析、优化、执行阶段,将SELECT的查询结果直接返回到客户端,加速SELECT语句的执行。
Query cache中是利用Query_cache_block对象进行存储的,存储结构如下图:
+----------------------------------------------------------------------------+ | Block header | +----------------------------------------------------------------------------+ | Table of database table lists | +----------------------------------------------------------------------------+ | Type depended header | +----------------------------------------------------------------------------+ | Data ... | +----------------------------------------------------------------------------+说明:
Block header:指的就是Query_cache_block这个结构体自身需要存储的一些信息。详细信息可以参考sql/sql_cache.h中的结构体说明。
Table of database table lists:这部分是用来记录与Query相关的所有数据表header信息(Query_cache_block_table结构)。对于一条Query来说,每个相关的数据表都有一个这样的header信息。而对于结果集数据块来说,这部分不包含数据。
Type depended header :这部分用来记录依赖于该Block的具体对象header,比如是Query_cache_query还是Query_cache_result。
Data:顾名思义,就是记录我们当前存储对象的数据信息。
Query cache依靠自己对内存进行管理。当用户调整query_cache_size系统参数时,Query cache会自动的修改内存大小。
Query cache的内存初始化包含两个部分:
一次性申请query_cache_size大小的内存空间。
初始化内存管理方式。
Query cache的内存管理方式如下图所示:
说明:
Query cache对于整个内存的管理是将整个内存块划分多层大小不同的多个query_cache_memory_bins(简称bins)。具体的层数由query_cache_memory_bin_steps (简称steps)确定,steps是动态根据query_mem_size确定的,如何确定steps请参考Query_cache::init_cache。第N层的bins数量是由(前N-1层bins数量总和 + QUERY_CACHE_MEM_BIN_PARTS_INC) * QUERY_CACHE_MEM_BIN_PARTS_MUL 确定的。QUERY_CACHE_MEM_BIN_PARTS_INC和QUERY_CACHE_MEM_BIN_PARTS_MUL是定义的控制每一层bins增量的两个宏。
Query cache刚初始化后,整个内存作为一个大的free_block。当需要申请新的数据块时,Query cache首先寻找大小最为接近的bins进行匹配,判断bins中有没有free_block。如果有则使用;如果没有则从其上一个bins中寻找free_block(bins是按照空间大小降序排列的,上层的bins大小要比下层的bins空间大),直到找到合适大小的一个。 如果最后没有找到合适大小的,Query cache将启动淘汰机制进行淘汰,以满足空间申请的需求。
在需要申请内存块时,如果没有找到合适大小的free block, Query cache将启动淘汰机制来淘汰最旧的缓存记录,释放的block将被分配到对于大小的bins中。如果分配过程中发现当前回收的free block和bins中free block地址相邻, 当前回收的free block将会和相邻的free block进行合并,重新插入到大小合适的bins中。另外Query cache通过flush query cache语句可以自动整理Query cache的defragments。
功能: 当前sql语句忽略使用query cache.
功能: 当query_cache_type为ON或者DEMAND时,考虑缓存当前sql语句的结果集。
为了快速查找,Query cache中维护着一个HASH map,用来记录Query对应的其所在的query_cache_block。其对应的Key由query + database + flag(包含影响执行结果的所有环境变量)组成。如果当前Query与任何Key比较一致的话,对应的结果集就可以被直接返回。
当一个表发生改变时,所有与该表相关的cached queries将失效。一个表发生变化,包含多种语句,比如 INSERT, UPDATE, DELETE, TRUNCATE TABLE,ALTER TABLE, DROP TABLE, 或者 DROP DATABASE。 为了加速失效过程,Query cache中维护着一个HASH map, 用来记录table对应的query_cache_block_table。其Key由db_name + table_name构成。通过Key的比较可以获取query_cache_block_table,该对象包含一个双向链表,记录了所有与该表相关的cached queries。通过这样的方式便可以快速的失效一个表对应的所有cached queries。
如果当前用户频繁使用同样的query进行查询,Query cache对于性能的提升是显而易见的。但是也有不太理想的情况,Query cache由于不仅依赖于执行的query,同时也依赖于当时的执行环境,比如SQL_MODE信息,字符集等,因此对于多用户共享Query cache,很难做到完全的共享。
另外,对于失效方式,目前MySQL做的并不到位,只要是表或表中的数据发生了变化便会引发相关cached queries失效,而更理想的方式是只有影响当前cached queries的执行结果的表的变化才引发相关cached plan失效。比如如果UPDATE语句并未修改某个cached queries的执行记过,其实可以不用对其失效。
我们会在接下来的文章中继续剖析Query cache的并发处理过程,敬请期待。
