query cache 的内存使用和调优

本文探讨MySQL Query Cache的工作原理及其调优方法,包括如何提高缓存命中率、插入率及减少碎片化等问题。
mysql 的query cache 大小设置( query_cache_size)最小只能设置为40k, 且bytes数最好设置为1024的倍数,并且要注意 当 query_cache_type为0时,mysql也会为query cache分配 query_cache_size大小的内存,这个就可能不是你想要的了。
mysql 的query cache 是完全存放在内存中的,当mysql 重起的时候,cache 中的内容会完全丢失。
在mysql 启动初始化的时候,会分配一整块连续的( query_cache_size大小的)内存.在系统运行过程中,缓存数据时,会从这块内存中分配最小为query_cache_min_res_unit大小的,且每次分配的都会是这个参数的整数倍大小的内存区块。
除了这些存放缓存数据的block外,还有两个hash表,一个hash表存放hash过的 sql语句,另一个存放着cache过的这些语句所引用到的 数据库表,当数据库中有表改动的时候,就需要check一下这个hash表看是否需要使 query cache中相关的缓存失效(query cache invalidation)
关于一些 query cache相关的 系统变量可以参见以下(从相关书籍文档中直接copy过来的没有翻译):
Query Cache System Variables:
query_cache_limit: Result sets larger than this are not cached. The default is 1 Mb.
query_alloc_block_size: Specifies the size of memory blocks allocated by the
query cache during query processing. The default is 8 Kb.
query_cache_min_res_unit:Specifies the minimum size for blocks in the query
cache. The default value is 4 Kb.
query_cache_size: The total memory allocated by mysqld for the query
cache. The default is 0, which disables the query cache.
query_cache_type: Determines the mode of operation of the query cache. Options are OFF,
ON, and DEMAND. The default is ON.
query_cache_wlock_invalidate: If set to TRUE, queries referencing MyISAM tables are invalidated
when a write lock for that table is obtained, even if none of the data
is changed when the write lock is released. The default is FALSE.
query_cache_prealloc_size: Specifies the size of the buffer used for query parsing
by the cache. Defaults to 8 Kb.
还有一些状态信息的变量(Status Variable):
Query Cache Status Variables:
Qcache_free_blocks: Number of memory blocks free in the query cache
Qcache_free_memory : Total bytes of memory free in the query cache
Qcache_hits: Number of times a query matched the query cache
Qcache_inserts: Number of times a query and result set were inserted into the query cache
Qcache_lowmem_prunes: Number of times a query was removed due to query pruning
Qcache_not_cached: Number of queries that could not be cached
Qcache_queries_in_cache: Number of queries currently stored in the query cache
Qcache_total_blocks: Total number of memory blocks in cache
一般query cache tuning 所做的就是要针对这些 status variables 调整那些system variables,这些变量和状态信息都可用 SHOW GLOBAL Variables或SHOW GLOBAL STATUS 查看,在INFORMATION_SCHEMA中也可以查看。
下面我们就用以上的状态信息看看一些指标(我会列举某个系统中的一些实际数据,版本为 5.0.45)。
1:缓存命中率(query cache hit ratio)。这个指标的计算公式是缓存命中次数 除以 命中次数与未命中次数(Com_select即为 未从cache中找到缓存并执行查询计划的语句)的总和
Qcache_hits / (Qcache_hits + Com_select)
223254326 /(223254326 +38402673) * 100%= 85.32%
2:缓存的 插入率 (insert ratio)
Qcache_inserts / Com_select
37878146/38402673 * 100% = 98.63%
这个百分比比较高,说明大部分select语句都放入到了query cache, 再来看一下Qcache_lowmem_prunes这个数值的大小 为 70013,看来情况还是很好的,在98.63%的语句都放入了query cache里面的情况下 ,pruning次数还是很少的。
3: 空缓存所占百分比,可以分 字节 和 块计算。
Qcache_free_memory / query_cache_size * 100%
Qcache_free_blocks / Qcache_total_blocks * 100%
如果空缓存率很高,那么就应该减少 query_cache_size , 或者 cache更多的查询。
如果这个值较低,而同时 Qcache_queries_in_cache 也比较低,那么就有可能是以下几种情况了:
1)query_cache_size需要增大来缓存更多的查询。
2)query_cache_limit 需要减少,来减少打数据结果集的语句。
3)query cache 中出现了很多碎片,需要清理了。

我们下面先看看怎么cache更多的查询,然后讨论一下碎片的问题。
当你认为你的query cache 的memory 利用率不够的话,可以采用以下几种方式cache更多查询。
1)尽少利用 SELECT SQL_NO_CACHE (query_cache_type 如果是ON的话)
2)尽多地利用SELECT SQL_CACHE (query_cache_type 如果是DEMAND的话)
3)Qcache_not_cached是不能被缓存的查询,如果这个数值很大的话,加大query_cache_limit的值来让有更大结果集的查询放入到缓存。
4)如果Qcache_lowmem_prunes 和Qcache_free_memory 值都很高的话,那很有可能是出现碎片了,那么就需要清理query cache了。

在mysql中主要有两方面导致了碎片的出现:
1)前面说过,query cache在使用时是按照block分配的,那么结果集大小不可能正好合适。query cache的管理程序一开始是不知道结果集大小的,因为他接受数据是row by row的,当所有结果集都存放到缓存中后,管理程序就会裁减一下一开始分配的内存而使其正好和结果集大小一致。
2)其次就是由于query cache的失效(invalidation )。缓存失效时,缓存的数据会从query cache 中删除,这样就出现了query cache的空洞(hole)。
怎么样来避免query cache 碎片化呢?
一个方法就是调整query cache 的block 大小,将其设置为所有结果集大小的平均值:
(query_cache_size - Qcache_free_memory)/Qcache_queries_in_cache
当然这只是一般的方法,有些时候是不是用的,比如结果集有很多很大的也有很多很小的。
另外一个方法就是减小query cache 的prune次数 和 invalidation的次数。怎么减小就是一个比较复杂的问题了。当不能减少UPDATE, INSERT和 DELETE语句的频度时,可以将大表分成小表,也可以用merge table或者partitioning。
当很难避免碎片时,我们可以把query_cache_type设置为DEMAND , 用SQL_CACHE缓存你想缓存的查询。出现了碎片后,我们可以用 FLUSH QUERY CACHE 语句整理一下query cache, 这条语句不会使query cache里的数据丢失,它只是整理内存去掉碎片(defragment),但是要注意如果 query_cache_size设置得比较大的话 ,这段时间将会比较久,而这段时间内几乎所有的查询语句和别的DML 、DDL语句都会等待defragment的完成,所以做这个操作的时候请be careful!
最后,如果要清除掉query cache里面的内容的话可以用 RESET QUERY CACHE 。
<think>嗯,用户想化MySQL 8.0.39在32G内存服务器上的性能。这是一个相当专业的数据库问题,需要系统性地分析内存分配、查询配置整。用户可能是个运维工程师或者DBA,正在为生产环境做性能化,语气中透露出实用主义倾向——ta要的是可立即操作的解决方案,而不是理论探讨。 从引用内容看,用户似乎关注过tuning-primer.sh脚本(引用1)、内存表限制(引用2)表锁状态(引用3),这些线索很重要。特别是引用2提到的"避免磁盘临时表""BLOB/TEXT内存限制",暗示用户可能遇到查询临时表溢出或大字段处理问题。不过引用信息比较零碎,需要结合MySQL 8的特性整合。 首先得建立化框架:内存配置永远是核心,32G内存要分给InnoDB缓冲池、查询缓存、连接线程等。根据经验,独立服务器上MySQL可占用70-80%物理内存,所以缓冲池设22G左右比较安全(留足OS突发需求空间)。但MySQL 8新增了redo log原子DDL,默认配置可能偏保守。 注意到用户没提具体负载类型。是OLTP还是OLAP?高并发短查询还是复杂报表?这会影响连接线程池设置。先按通用场景建议,但必须强监控的重要性——比如用performance_schema看哪些查询消耗资源最多。 查询化层面特别要警惕临时表(引用2的痛点)。当看到Created_tmp_disk_tables飙升时,要么整tmp_table_size/max_heap_table_size,要么重写用到BLOB/TEXT或超大GROUP BY的查询。内存表限制在MySQL 8里依然存在,这点必须提醒用户。 表锁问题(引用3)在InnoDB引擎下通常不严重,但若出现Table_locks_waited较高,可能暗示需要分区或化事务提交频率。MySQL 8的元数据锁机制也有改进,可建议开启lock_wait_timeout避免卡死。 最后要给出可验证的路径:不是扔一堆参数就完事,而是教用户用SHOW STATUS慢查询日志验证效果。比如完缓冲池后看Innodb_buffer_pool_read_requests命中率是否>95%,这才是关键。 突然想到用户可能跳过版本升级说明。MySQL 8.0默认的caching_sha2_password认证比旧版慢,如果客户端没更新驱动,可能拖累连接速度。这个坑得填上。 相关问题的设计要引导用户深入:从内存配置延伸到监控工具,再到索引复制配置,形成完整的学习路径。特别是第4问"如何识别低效SQL"——毕竟90%的性能问题根源在查询而非配置。</think>以下是针对 MySQL 8.0.39 在 32GB 内存服务器上的性能化指南,结合关键配置监控策略: --- ### **一、内存分配化(核心)** 1. **InnoDB 缓冲池** - 分配物理内存的 60-70%: ```ini innodb_buffer_pool_size = 20G # 建议 20-22G ``` - 监控命中率: ```sql SHOW STATUS LIKE 'Innodb_buffer_pool_read%'; -- 目标:`Innodb_buffer_pool_read_requests` / (`Innodb_buffer_pool_reads` + `Innodb_buffer_pool_read_requests`) > 99% ``` 2. **临时表与排序** - 避免磁盘临时表(引用[2]): ```ini tmp_table_size = 128M max_heap_table_size = 128M ``` - 监控磁盘临时表: ```sql SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables'; # 目标趋近于0 ``` 3. **连接线程与缓存** ```ini thread_cache_size = 32 # 减少线程创建开销 table_open_cache = 4096 # 避免频繁开表 max_connections = 150 # 根据实际并发整 ``` --- ### **二、关键参数整** 1. **日志与写入化** ```ini innodb_log_file_size = 2G # 增大日志减少刷写 innodb_flush_log_at_trx_commit = 1 # 数据安全先(若可容忍丢失改为2) innodb_flush_method = O_DIRECT # 避免双缓冲 ``` 2. **查询缓存** > MySQL 8.0 已移除查询缓存,无需配置。 3. **锁与并发控制** ```ini innodb_lock_wait_timeout = 30 # 避免长锁阻塞 performance_schema = ON # 启用监控 ``` --- ### **三、监控与诊断工具** 1. **内置工具** ```sql -- 表锁争用监控(引用[3]) SHOW GLOBAL STATUS LIKE 'Table_locks%'; -- 查看 InnoDB 状态 SHOW ENGINE INNODB STATUS; ``` 2. **性能分析脚本** 使用 `tuning-primer.sh`(引用[1]): ```bash wget https://launchpad.net/mysql-tuning-primer/trunk/1.6-r1/+download/tuning-primer.sh chmod +x tuning-primer.sh ./tuning-primer.sh ``` 3. **慢查询日志** ```ini slow_query_log = ON long_query_time = 1 # 记录超过1秒的查询 log_queries_not_using_indexes = ON ``` --- ### **四、进阶化建议** 1. **索引化** - 定期执行 `ANALYZE TABLE` - 使用 `EXPLAIN` 分析慢查询 - 删除未使用索引: ```sql SELECT * FROM sys.schema_unused_indexes; ``` 2. **分区表** 对超大型表按时间或范围分区,减少单次扫描数据量。 3. **升级到 MySQL 8.0 最新子版本** 修复已知性能问题(如 8.0.34+ 化了并行复制)。 --- ### **五、验证化效果** 1. 压力测试工具: ```bash sysbench oltp_read_write --table-size=1000000 run ``` 2. 监控指标: - CPU 使用率(目标 < 70%) - 内存交换(`vmstat` 中 `si/so` 趋近 0) - 磁盘 I/O 等待(`iostat` 中 `%util` < 80%) > **重要提示**:每次仅修改 1-2 个参数,通过基准测试对比效果。配置文件路径通常为 `/etc/my.cnf` 或 `/etc/mysql/mysql.conf.d/mysqld.cnf`。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值