MySQL缓存Query Cache (转)

本文深入探讨MySQL Query Cache的工作原理,包括其存储机制、调整方法及关键配置参数。同时,介绍了如何根据实际情况合理启用或禁用Query Cache以提高数据库性能。
原理

QueryCache(下面简称QC)是根据SQL语句来cache的。一个SQL查询如果以select开头,那么MySQL服务器将尝试对其使用QC。每个Cache都是以SQL文本作为key来存的。在应用QC之前,SQL文本不会被作任何处理。也就是说,两个SQL语句,只要相差哪怕是一个字符(例如大小写不一样;多一个空格等),那么这两个SQL将使用不同的一个CACHE。

不过SQL文本有可能会被客户端做一些处理。例如在官方的命令行客户端里,在发送SQL给服务器之前,会做如下处理:

  • 过滤所有注释
  • 去掉SQL文本前後的空格,TAB等字符。注意,是文本前面和後面的。中间的不会被去掉。

下面的三条SQL里,因为SELECT大小写的关系,最後一条和其他两条在QC里肯定是用的不一样的存储位置。而第一条和第二条,区别在于後者有个注释,在不同客户端,会有不一样的结果。所以,保险起见,请尽量不要使用动态的注释。在PHP的mysql扩展里,SQL的注释是不会被去掉的。也就是三条SQL会被存储在三个不同的缓存里,虽然它们的结果都是一样的。

select * FROM people where name='surfchen'; 
select * FROM people where /*hey~*/name='surfchen'; 
SELECT * FROM people where name='surfchen'; 

目前只有select语句会被cache,其他类似show,use的语句则不会被cache。

因为QC是如此前端,如此简单的一个缓存系统,所以如果一个表被更新,那么和这个表相关的SQL的所有QC都会被失效。假设一个联合查询里涉及到了表A和表B,如果表A或者表B的其中一个被更新(update或者delete),这个查询的QC将会失效。

也就是说,如果一个表被频繁更新,那么就要考虑清楚究竟是否应该对相关的一些SQL进行QC了。一个被频繁更新的表如果被应用了QC,可能会加重数据库的负担,而不是减轻负担。我一般的做法是默认打开QC,而对一些涉及频繁更新的表的SQL语句加上SQL_NO_CACHE关键词来对其禁用CACHE。这样可以尽可能避免不必要的内存操作,尽可能保持内存的连续性。

那些查询很分散的SQL语句,也不应该使用QC。例如用来查询用户和密码的语句——“select pass from user where name='surfchen'”。这样的语句,在一个系统里,很有可能只在一个用户登陆的时候被使用。每个用户的登陆所用到的查询,都是不一样的SQL文本,QC在这里就几乎不起作用了,因为缓存的数据几乎是不会被用到的,它们只会在内存里占地方。

存储块

在本节里“存储块”和“block”是同一个意思

QC缓存一个查询结果的时候,一般情况下不是一次性地分配足够多的内存来缓存结果的。而是在查询结果获得的过程中,逐块存储。当一个存储块被填满之後,一个新的存储块将会被创建,并分配内存(allocate)。单个存储块的内存分配大小通过query_cache_min_res_unit参数控制,默认为4KB。最後一个存储块,如果不能被全部利用,那么没使用的内存将会被释放。如果被缓存的结果很大,那么会可能会导致分配内存操作太频繁,系统系能也随之下降;而如果被缓存的结果都很小,那么可能会导致内存碎片过多,这些碎片如果太小,就很有可能不能再被分配使用。

除了查询结果需要存储块之外,每个SQL文本也需要一个存储块,而涉及到的表也需要一个存储块(表的存储块是所有线程共享的,每个表只需要一个存储块)。存储块总数量=查询结果数量*2+涉及的数据库表数量。也就是说,第一个缓存生成的时候,至少需要三个存储块:表信息存储块,SQL文本存储块,查询结果存储块。而第二个查询如果用的是同一个表,那么最少只需要两个存储块:SQL文本存储块,查询结果存储块。

通过观察Qcache_queries_in_cache和Qcache_total_blocks可以知道平均每个缓存结果占用的存储块。它们的比例如果接近1:2,则说明当前的query_cache_min_res_unit参数已经足够大了。如果Qcache_total_blocks比Qcache_queries_in_cache多很多,则需要增加query_cache_min_res_unit的大小。

Qcache_queries_in_cache*query_cache_min_res_unit(sql文本和表信息所在的block占用的内存很小,可以忽略)如果远远大于query_cache_size-Qcache_free_memory,那么可以尝试减小query_cache_min_res_unit的值。

调整大小

如果Qcache_lowmem_prunes增长迅速,意味着很多缓存因为内存不够而被释放,而不是因为相关表被更新。尝试加大query_cache_size,尽量使Qcache_lowmem_prunes零增长。

启动参数

show variables like 'query_cache%'可以看到这些信息。

query_cache_limit
如果单个查询结果大于这个值,则不Cache
query_cache_size
分配给QC的内存。如果设为0,则相当于禁用QC。要注意QC必须使用大约40KB来存储它的结构,如果设定小于40KB,则相当于禁用QC。QC存储的最小单位是1024 byte,所以如果你设定了一个不是1024的倍数的值,这个值会被四舍五入到最接近当前值的等于1024的倍数的值。
query_cache_type
0 完全禁止QC,不受SQL语句控制(另外可能要注意的是,即使这里禁用,上面一个参数所设定的内存大小还是会被分配);1启用QC,可以在SQL语句使用SQL_NO_CACHE禁用;2可以在SQL语句使用SQL_CACHE启用。
query_cache_min_res_unit
每次给QC结果分配内存的大小

状态

show status like 'Qcache%'可以看到这些信息。

Qcache_free_blocks
当一个表被更新之後,和它相关的cache blocks将被free。但是这个block依然可能存在队列中,除非是在队列的尾部。这些blocks将会被统计到这个值来。可以用FLUSH QUERY CACHE语句来清空free blocks。
Qcache_free_memory
可用内存,如果很小,考虑增加query_cache_size
Qcache_hits
自mysql进程启动起,cache的命中数量
Qcache_inserts
自mysql进程启动起,被增加进QC的数量
Qcache_lowmem_prunes
由于内存过少而导致QC被删除的条数。加大query_cache_size,尽可能保持这个值0增长。
Qcache_not_cached
自mysql进程启动起,没有被cache的只读查询数量(包括select,show,use,desc等)
Qcache_queries_in_cache
当前被cache的SQL数量
Qcache_total_blocks
在QC中的blocks数。一个query可能被多个blocks存储,而这几个blocks中的最後一个,未用满的内存将会被释放掉。例如一个QC结果要占6KB内存,如果query_cache_min_res_unit是4KB,则最後将会生成3个blocks,第一个block用来存储sql语句文本,这个不会被统计到query+cache_size里,第二个block为4KB,第三个block为2KB(先allocate4KB,然後释放多馀的2KB)。每个表,当第一个和它有关的SQL查询被CACHE的时候,会使用一个block来存储表信息。也就是说,block会被用在三处地方:表信息,SQL文本,查询结果。

排序缓冲

当一个查询需要对结果进行排序的时候,MySQL会分配一定的内存用来排序。这个内存大小由sort_buffer_size来控制。记得,这个参数是针对每个查询的,而不是所有查询总共可分配的量。

如果sort_buffer_size不够大,排序的结果将会被分段写入临时文件里。每次结束之後再把文件中的排序结果拿出来合并,进行再次排序,直到得出最後结果。sort_buffer_size越小,合并的次数就越多。合并次数可以通过状态变量Sort_merge_passes获得。理论上,Sort_merge_passes越小,排序越快。但是在实际应用中可能并非如此。sort_buffer_size如何设置需要根据实际运行环境来进行测试。如果实在不知道如何测试,那么就设到使Sort_merge_passes为0吧。

read_buffer_size read_rnd_buffer_size join_buffer_size thread_cache

字段选择

  • 从二进制角度考虑
  • select col1,col2 from table PROCEDURE ANALYSE();,这条语句可以根据当前表的内容来给出一个字段类型的推荐。

MyISAM

key_buffer_size

CACHE INDEX Syntax

innodb

innodb_buffer_pool_size
这是和innodb有关的最重要的一个参数。这个参数指定了innodb缓存池的大小。这个缓存池被用来存储

innodb_file_per_table innodb_additional_mem_pool_size=80M innodb_log_file_size=1G innodb_log_buffer_size=16M innodb_flush_method=O_DIRECT

 

(max_connections * (sort_buffer_size + read_buffer_size + read_rnd_buffer_size + join_buffer_size)) + key_buffer + innodb_bufer_pool_size + query_cache + tmp_table_size

系统相关

linux:

echo -n 0 >/proc/sys/vm/swappiness

huge page

memlock

优化工具

mysqltuner

<think>我们正在讨论MySQL的查询计划缓存Query Plan Cache)。根据用户的问题,我们需要提供Query Plan Cache的定义和相关信息。需要注意的是,MySQL中并没有一个名为“Query Plan Cache”的独立缓存,但通常我们提到的查询缓存Query Cache)和准备语句的缓存(Prepared Statement Cache)与查询计划相关。 然而,在MySQL中,查询缓存Query Cache缓存的是整个SELECT查询的结果集,而不是查询计划本身。而对于查询计划缓存,实际上在MySQL 8.0之前,存储过程有查询计划缓存,但对于一般的SQL语句,MySQL并不会缓存查询计划(除了准备语句)。 在MySQL 8.0中,引入了执行计划缓存(Execution Plan Cache)用于准备好的语句(prepared statements)。所以,用户可能指的是这个。 因此,我们将重点介绍MySQL中的执行计划缓存(用于准备语句)以及可能相关的查询缓存(但注意,查询缓存已在MySQL 8.0中被移除)。 由于用户的问题是关于“Query Plan Cache”,我们主要讨论执行计划缓存(针对准备语句)。 定义: 执行计划缓存(Execution Plan Cache)是MySQL中用于存储预处理语句(prepared statements)的执行计划的一种缓存机制。当相同的预处理语句再次执行时,MySQL可以重用之前编译好的执行计划,从而跳过优化和编译阶段,提高执行效率。 详细介绍: 1. 缓存对象:执行计划缓存针对的是预处理语句(Prepared Statements)。预处理语句是通过`PREPARE ... FROM`创建的,或者通过客户端驱动程序(如Connector/J、Connector/Python等)以二进制协议发送的语句(通常使用参数化查询)。 2. 工作原理: - 当预处理语句第一次执行时,MySQL会进行解析、优化,并生成执行计划,然后将执行计划缓存起来。 - 后续执行相同的预处理语句时,MySQL会直接使用缓存的执行计划,只需将参数替换即可执行,从而节省了优化时间。 3. 缓存管理: - 缓存大小由系统变量`cached_prepared_statements`控制(在MySQL 5.7中,这个变量是`max_prepared_stmt_count`,但在MySQL 8.0中,执行计划缓存的管理有所变化)。 - 在MySQL 8.0中,执行计划缓存的大小由系统变量`prepared_statement_cache_size`(会话级别)和`prepared_statement_cache_size_limit`(全局限制)控制。 - 缓存采用LRU(最近最少使用)策略进行管理。 4. 注意事项: - 执行计划缓存只对预处理语句有效,普通的查询语句不会缓存执行计划。 - 当表结构发生变化(如ALTER TABLE)或数据分布发生显著变化(导致统计信息更新)时,相关的执行计划会被自动从缓存中移除,以确保执行计划的正确性。 - 执行计划缓存是会话级别的,但全局参数可以限制总的缓存大小。 5. 与查询缓存的区别: - 查询缓存Query Cache缓存的是查询结果集,而执行计划缓存缓存的是执行计划(即如何执行查询的步骤)。 - 查询缓存MySQL 8.0中被移除,而执行计划缓存仍然存在。 6. 相关系统变量(以MySQL 8.0为例): - `prepared_statement_cache_size`: 每个会话缓存的预处理语句数量(默认值根据文档可能有所不同,通常为256)。 - `prepared_statement_cache_size_limit`: 全局限制,防止所有会话的总缓存占用过多内存(默认值通常为2MB,但实际存储的是计划,不是结果)。 7. 监控: - 可以通过`SHOW STATUS LIKE 'Com_prepare_sql'`和`SHOW STATUS LIKE 'Com_execute_sql'`来查看预处理语句的准备工作次数和执行次数。 - 状态变量`Prepared_stmt_count`显示当前系统中的预处理语句数量(包括缓存的)。 需要注意的是,在MySQL 5.7及之前,执行计划缓存的行为和配置有所不同。 由于用户引用的内容中提到了EXPLAIN和SHOW PROCESSLIST等,这些虽然与查询计划有关,但不是直接针对执行计划缓存的。因此,我们主要围绕上述内容回答。 根据引用[1]中的内容,EXPLAIN只能解释SELECT操作,而且部分统计信息是估算的,这也说明了为什么执行计划缓存很重要,因为重复的预处理语句可以避免重复优化。 现在,我们将回答组织如下: </think>### MySQL 查询计划缓存Query Plan Cache)的定义与介绍 #### 定义 MySQL **查询计划缓存**(Query Plan Cache)是一种优化机制,用于存储已解析和优化后的SQL语句执行计划。当相同的SQL语句重复执行时,MySQL可直接重用缓存的执行计划,**跳过语法解析、语义检查和优化器计算等步骤**,从而显著提升查询性能。该缓存主要针对**预处理语句(Prepared Statements)**。 #### 核心特性 1. **缓存对象** - 仅缓存预处理语句(通过`PREPARE`创建或应用程序参数化查询)的优化执行计划。 - 普通即时查询(Ad-hoc Query)**不缓存**执行计划。 2. **缓存生命周期** - 会话级缓存:默认绑定到客户端会话,会话结束时自动释放。 - 全局管理:通过参数控制全局缓存总量(如`prepared_statement_cache_size`)。 3. **缓存失效机制** 当以下操作发生时,相关执行计划自动失效: - 表结构变更(`ALTER TABLE`) - 索引添加/删除 - 统计信息更新(如`ANALYZE TABLE`) - 数据量剧烈变化(导致优化器重新选择索引)[^1] 4. **性能提升原理** ![执行计划缓存对比](https://www.mysqltutorial.org/wp-content/uploads/2018/11/MySQL-Prepared-Statement-Execution-Plan-Cache.png) 首次执行需完整流程(解析→优化→执行),后续执行**直接跳至执行阶段**。 #### 相关配置参数 | 参数名 | 作用 | 默认值 | |---------------------------------|----------------------------------------------------------------------|---------------| | `prepared_statement_cache_size` | 单个会话最大缓存的预处理语句数量 | 256(条) | | `performance_schema_max_prepared` | 监控缓存的性能模式指标数量 | 动态调整 | | `max_prepared_stmt_count` | 全局最大预处理语句数量(含缓存和活跃语句) | 16,382 | #### 局限性 1. **不适用于即时查询** 如`SELECT * FROM users WHERE id=1;`这类非参数化查询无法利用缓存[^1]。 2. **内存开销** 每个缓存的执行计划约占用10-100KB内存,需权衡性能与资源消耗。 3. **动态环境适应性差** 数据分布频繁变化时,缓存可能保留非最优计划,需手动清除(`DEALLOCATE PREPARE`)[^1]。 #### 操作示例 ```sql -- 创建预处理语句(自动缓存执行计划) PREPARE user_query FROM 'SELECT * FROM users WHERE age > ?'; -- 首次执行(解析+优化+执行) SET @age_threshold = 30; EXECUTE user_query USING @age_threshold; -- 后续执行(直接使用缓存计划) SET @age_threshold = 40; EXECUTE user_query USING @age_threshold; -- 手动释放缓存 DEALLOCATE PREPARE user_query; ``` #### 监控方法 ```sql -- 查看缓存命中率(Com_stmt_execute vs Com_stmt_prepare) SHOW GLOBAL STATUS LIKE 'Com_stmt%'; ``` - **高缓存命中率标志**:`Com_stmt_execute` >> `Com_stmt_prepare` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值