视频地址:MYSQL缓存查询原理和优缺点_哔哩哔哩_bilibili
一 缓存查询过程
【缓存查询过程】
【查询缓存是否开启】
have_query_cache | 作用:显示服务器是否支持查询缓存。 返回值: YES:表示服务器支持查询缓存。 NO:表示服务器不支持查询缓存。 注意事项: MySQL 8.0 及以上版本:从 MySQL 8.0 开始,查询缓存功能已被移除,因此 have_query_cache 将始终返回 NO。 MySQL 5.7 及更早版本:在这些版本中,查询缓存仍然可用,但默认是关闭的。 |
query_cache_limit | 作用:限制单个查询结果可以占用的最大缓存空间。 单位:字节。 默认值:1MB。 范围:1 到 query_cache_size。 |
query_cache_min_res_unit | 作用:设置分配给每个缓存块的最小内存单元大小。 单位:字节。 默认值:4KB。 范围:4KB 到 1MB。 |
query_cache_size | 作用:设置查询缓存所占用的最大内存大小。 单位:字节。 默认值:在 MySQL 5.7 中默认为 0MB(禁用),在更早版本中默认为 4MB。 范围:0 到 1GB。 |
query_cache_type | 作用:控制是否启用查询缓存。 可选值: OFF:完全禁用查询缓存。 ON:开启查询缓存,所有非唯一查询都将被缓存。 DEMAND(默认值):只有通过 SQL_CACHE 显式标记的查询才会被缓存。 |
query_cache_wlock_invalidate | 作用:控制写锁是否使缓存失效。 可选值: 0:写锁不会使缓存失效。 1(默认值):写锁会使缓存失效。 |
【缓存统计参数查看】
Qcache_free_blocks | 描述:查询缓存中空闲块的数量。 用途:反映查询缓存内部可用空间的情况,有助于理解缓存的空间利用率。 |
Qcache_free_memory | 描述:查询缓存中剩余的自由内存数量(以字节为单位)。 用途:表示查询缓存还有多少可用空间,可用于新的查询结果的存储。 |
Qcache_hits | 描述:查询缓存命中的次数。 用途:衡量查询缓存的有效性,即有多少次查询可以直接从缓存中获取结果而不需要重新执行。 |
Qcache_inserts | 描述:查询结果插入到缓存中的次数。 用途:表示有多少次查询的结果被添加到了查询缓存中。 |
Qcache_lowmem_prunes | 描述:由于内存不足而从缓存中删除的查询结果的数量。 用途:当查询缓存内存不足时,系统会自动删除部分缓存条目来释放空间。这个计数器反映了这种行为发生的频率。 |
Qcache_not_cached | 描述:无法被缓存的查询数量(例如包含 SQL_NO_CACHE 的查询)。 用途:帮助理解为什么某些查询没有被缓存。 |
Qcache_queries_in_cache | 描述:当前在缓存中的查询数量。 用途:反映查询缓存中存储了多少个不同的查询结果。 |
Qcache_total_blocks | Qcache_total_blocks 描述:查询缓存中总块的数量。 用途:表示查询缓存的物理存储结构的规模。 |
【开启缓存】
my.ini配置
[mysqld]
query_cache_type = 1
query_cache_size = 1M
二 缓存优缺点
【优点】
1、减少磁盘I/O操作:由于Query Cache是基于内存的缓存机制,可以直接从内存中获取查询结果,避免了频繁访问磁盘以获取数据,从而显著降低了磁盘I/O操作的需求。
2、减轻CPU计算负担:Query Cache能够在权限验证后立即尝试从缓存中返回结果,这意味着许多查询不需要经过优化器的分析和优化过程,也不需要与存储引擎进行交互,这大大减少了CPU的计算工作量。
【缺点】
- 哈希值索引的局限性:MySQL使用一个类似于
HashMap
的数据结构来存储查询缓存,通过一个哈希值索引来定位缓存项。这个哈希值由查询本身、当前查询的数据库、客户端协议、版本号等信息计算得出。这意味着即使是微小的变化(如空格、注释等)也会导致不同的哈希值,从而无法命中缓存。 - 查询字符串严格匹配:在判断是否命中缓存之前,MySQL不会解析SQL语句。它直接使用原始的SQL字符串来查找缓存,这意味着即使查询逻辑相同,只要字符串中有任何字符不同(包括空格、注释等),都会导致缓存命中失败。
- 不支持特定类型的查询:如果查询SQL中包含任何用户自定义函数、存储函数、用户变量、临时表或者MySQL库中的系统表,其查询结果都不会被缓存。例如,包含
LIKE
、NOW()
、CURRENT_DATE()
等函数的查询将不会被缓存。 - prepared statement的限制:在MySQL 5.1之前的版本中,使用预编译语句 (
prepared statement
) 的查询结果永远不会被缓存,即使参数完全相同也不会命中缓存。虽然在5.1版本后有所改进,但在实际应用中仍然可能存在限制。 - 结果集大小限制:如果查询返回的结果集太大(超过
query_cache_limit
配置的值),则不会被缓存。 - 分库分表环境下的无效性:在分库分表的环境下,MySQL查询缓存通常不起作用,因为查询可能跨多个分片执行,而缓存只能在单个数据库实例级别起作用。
- 触发器和自定义函数的影响:如果查询涉及到触发器或自定义函数,MySQL查询缓存也将失效。这是因为这些特性可能会改变数据状态,导致缓存数据过时。
- 日期和时间函数的优化建议:对于使用日期和时间函数的查询,如
CURRENT_DATE
或NOW
等,如果返回的是基于小时或天级别的数据,建议预先计算这些值并将其作为常量值传递到查询中,以提高缓存的命中率。 - 并发访问的共享特性:虽然查询缓存是通过sessions共享的,意味着一个客户端查询的缓存结果可以被其他客户端使用,但这可能导致数据一致性问题,特别是在高并发环境下。
- 缓存检查前置但不一定命中:每个查询语句在执行前都会先经过缓存检查,即便该查询最终无法命中缓存,这也增加了不必要的开销。
- 缓存存储带来的系统消耗:如果查询结果可以被缓存,那么在查询执行完成后,结果会被存储到缓存中,这一过程会带来额外的系统资源消耗。
- 写操作导致的缓存失效开销:当表中的数据发生变化时(例如通过 INSERT, UPDATE, DELETE 等操作),MySQL 必须清除与该表相关的所有缓存条目,如果缓存很大或存在大量碎片,这一过程可能导致显著的性能下降。
- 大缓存下的更新延迟:如果 query_cache 设置得非常大,并且该表的查询模式非常多变,那么查询缓存的失效速度较慢,因此即使是简单的 UPDATE 或 INSERT 操作也可能变得非常缓慢。
- 频繁的缓存失效:在表结构或数据发生改变时,如执行 INSERT, UPDATE, DELETE, TRUNCATE, ALTER TABLE, DROP DATABASE 等操作,查询缓存中的数据将不再有效,相应的缓存条目会被清空,这可能导致缓存的有效利用率较低。
- 空间消耗:缓存占用物理内存,如果缓存的数据集过大,则可能会消耗大量的内存资源。
- 不适合高写操作场景:在写操作频繁的环境中,由于每次写入都会导致缓存失效,因此查询缓存的有效性会迅速降低。
- 并发性能问题:在高并发环境下,频繁的缓存更新和失效可能导致性能瓶颈,尤其是在多核处理器上。
- 管理复杂度:正确配置和维护查询缓存需要较高的系统调优知识水平,以避免潜在的性能问题。
- 不支持所有查询类型:并非所有的查询都支持缓存,例如包含用户变量或某些函数的查询就不支持缓存。
在 MySQL 8.0 版本中,默认已经移除了查询缓存功能,因为它的维护成本高并且在很多情况下并不能显著提升性能