MySQL中的缓存机制
了解工具特性的目的是为了更好的去运用,其中的设计思想也可以借鉴~
先看总结:
缓存类型 | MySQL 5.7 及之前 | MySQL 8.0 | 作用 | 是否推荐 |
---|---|---|---|---|
查询缓存(Query Cache) | ✅ 支持(默认开启) | ❌ 已移除 | 缓存 SELECT 查询结果 | 不推荐(MySQL 8.0 移除) |
InnoDB Buffer Pool | ✅ 核心缓存 | ✅ 核心缓存 | 缓存表数据 & 索引,减少磁盘 I/O | ✅ 强烈推荐(调优关键) |
文件系统缓存(OS Cache) | ✅ 操作系统缓存 | ✅ 操作系统缓存 | 操作系统缓存磁盘数据 | 自动生效,无需配置 |
应用层缓存(Redis) | ❌ 不属于 MySQL | ❌ 不属于 MySQL | 缓存热点数据(如用户信息) | ✅ 推荐(补充方案) |
MySQL 的缓存机制,在不同版本和场景下有所不同,主要包括查询缓存(Query Cache,MySQL 5.7 及之前)、InnoDB Buffer Pool(缓冲池)、以及 MySQL 8.0 后的优化。
1. MySQL 查询缓存(Query Cache,MySQL 5.7 及之前)
(1)什么是查询缓存?
- MySQL 5.7 及更早版本提供了一个 查询缓存(Query Cache),它会 缓存 SELECT 查询的结果集。
- 当相同的 SQL 语句再次执行时,MySQL 可以直接从缓存返回结果,而不需要重新查询磁盘,从而提高查询速度。
(2)查询缓存的工作方式
- 缓存的是完整的 SELECT 查询及其结果(包括表数据)。
- 只有完全相同的 SQL(包括大小写、空格、参数)才会命中缓存。
- 如果表数据发生变化(INSERT/UPDATE/DELETE),相关查询缓存会被自动失效。
(3)查询缓存的配置
- 启用查询缓存(MySQL 5.7 默认开启,但 MySQL 8.0 移除了它):
SHOW VARIABLES LIKE 'query_cache%';
query_cache_type
:是否启用(ON
/OFF
/DEMAND
)。query_cache_size
:缓存大小(默认 1MB,建议 0 或适当大小)。
(4)查询缓存的缺点
适用场景有限:
- 只缓存 SELECT 查询,不缓存 INSERT/UPDATE/DELETE。
- 表数据一旦变化,相关缓存全部失效(导致缓存命中率低)。
- 高并发写入场景下,查询缓存可能成为性能瓶颈(因为要检查缓存一致性)。
- 缓存粒度太粗(整个查询结果缓存,而不是部分数据)。
MySQL 8.0 已移除查询缓存(query_cache_type
参数不存在):
- 官方认为查询缓存在现代高并发、高写入场景下收益不高,反而增加维护成本。
如何关闭查询缓存(5.7版本)?
永久关闭(修改配置文件,重启 MySQL 生效)
要 永久关闭查询缓存,需要修改 MySQL 的配置文件(my.cnf
或 my.ini
),然后 重启 MySQL 服务。
(1) 找到 MySQL 配置文件
Linux:通常位于
/etc/my.cnf
或/etc/mysql/my.cnf
。Windows:通常位于
MySQL 安装目录 / my.ini
。
(2) 修改配置文件
在 [mysqld]
部分添加或修改以下参数:
[mysqld] query_cache_type = 0 # 或 OFF(完全禁用查询缓存) query_cache_size = 0 # 缓存大小设为 0(确保禁用)
说明:
query_cache_type = 0
或OFF
:禁用查询缓存。
query_cache_size = 0
:确保查询缓存内存分配为 0。
(3) 重启 MySQL 服务
Linux:
sudo systemctl restart mysql # 或 sudo service mysql restart
Windows:
打开 服务管理器(Services.msc),找到 MySQL 服务,右键 重启。
(4) 验证是否关闭
登录 MySQL,执行:
SHOW VARIABLES LIKE 'query_cache%';
2. InnoDB 缓冲池(Buffer Pool,MySQL 5.7 & 8.0 核心缓存)
(1)什么是 Buffer Pool?
- InnoDB 存储引擎的核心缓存机制,用于 缓存表数据和索引数据,减少磁盘 I/O。
- 它是 MySQL 最重要的缓存,直接影响性能。
(2)Buffer Pool 的作用
缓存数据页(Data Pages):
- 当查询访问表数据时,InnoDB 会先检查 Buffer Pool 是否有该数据页(如
SELECT * FROM users WHERE id=1
)。 - 如果数据在 Buffer Pool 中(缓存命中),直接返回,避免磁盘读取。
- 如果不在 Buffer Pool 中(缓存未命中),则从磁盘加载到 Buffer Pool。
缓存索引页(Index Pages):
- B+ 树索引 也会被缓存在 Buffer Pool 中,加速查询。
支持预读(Read-Ahead)和脏页刷新(Dirty Page Flush):
- 预读:预测可能访问的数据页,提前加载到 Buffer Pool。
- 脏页:修改后的数据页会先在 Buffer Pool 中缓存,再异步刷回磁盘。
(3)Buffer Pool 的配置
查看 Buffer Pool 大小:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
- 默认值:通常为 128MB~1GB(取决于服务器内存)。
- 推荐设置:70%~80% 的可用物理内存(如果是纯 MySQL 服务器)。
调整 Buffer Pool:
SET GLOBAL innodb_buffer_pool_size = 4G; -- 视服务器内存而定
(4)Buffer Pool 的监控
SHOW ENGINE INNODB STATUS\G
- 查看 Buffer Pool 命中率(
BUFFER POOL AND MEMORY
部分):- 理想命中率 > 95%(表示大部分查询从内存读取,而不是磁盘)。
3. MySQL 8.0 的缓存优化
(1)移除了查询缓存(Query Cache)
- MySQL 8.0 彻底删除了
query_cache
相关功能(因为高并发写入场景下性能不佳)。 - 官方推荐依赖 InnoDB Buffer Pool 和应用层缓存(如 Redis)。
(2)优化了 InnoDB Buffer Pool
- 支持多个 Buffer Pool 实例(减少锁竞争):
SET GLOBAL innodb_buffer_pool_instances = 4; -- 建议 4~8 个(视 CPU 核心数)
- 更智能的预读和脏页管理。
(3)其他缓存优化
- 自适应哈希索引(Adaptive Hash Index):
- InnoDB 会自动为频繁访问的索引页构建 内存哈希索引,加速查找。
- 文件系统缓存(OS Cache):
- 即使 MySQL 缓存未命中,操作系统(如 Linux)也会缓存磁盘数据(Page Cache)。
4. 应用层缓存(扩展方案)
如果 MySQL 缓存(Buffer Pool)仍然不够快,可以考虑:
(1)Redis / Memcached
- 缓存热点数据(如用户信息、商品详情),减少 MySQL 查询。
- 比 MySQL 查询缓存更灵活(可以缓存任意数据,而不仅是 SQL 结果)。
(2)本地缓存(如 Caffeine、Guava Cache)
- Java 应用可以缓存部分数据,减少数据库访问。
注意缓存一致性问题!!
5. 总结:MySQL 缓存机制对比
缓存类型 | MySQL 5.7 及之前 | MySQL 8.0 | 作用 | 是否推荐 |
---|---|---|---|---|
查询缓存(Query Cache) | ✅ 支持(默认开启) | ❌ 已移除 | 缓存 SELECT 查询结果 | 不推荐(MySQL 8.0 移除) |
InnoDB Buffer Pool | ✅ 核心缓存 | ✅ 核心缓存 | 缓存表数据 & 索引,减少磁盘 I/O | ✅ 强烈推荐(调优关键) |
文件系统缓存(OS Cache) | ✅ 操作系统缓存 | ✅ 操作系统缓存 | 操作系统缓存磁盘数据 | 自动生效,无需配置 |
应用层缓存(Redis) | ❌ 不属于 MySQL | ❌ 不属于 MySQL | 缓存热点数据(如用户信息) | ✅ 推荐(补充方案) |
6. 实践
(1)MySQL 5.7 及之前
- 查询缓存(Query Cache):不建议依赖(命中率通常低,高并发写入时性能差)。
- InnoDB Buffer Pool:必须调优(
innodb_buffer_pool_size
建议设为 70%~80% 物理内存)。
(2)MySQL 8.0
- 查询缓存已移除,依赖 InnoDB Buffer Pool(调优
innodb_buffer_pool_size
和innodb_buffer_pool_instances
)。 - 结合 Redis / 应用层缓存 处理热点数据。
(3)通用优化建议
- 优先优化 SQL 和索引(比缓存更重要)。
- 调大
innodb_buffer_pool_size
(减少磁盘 I/O)。 - 监控 Buffer Pool 命中率(目标 > 95%)。
- 高并发场景考虑 Redis 缓存(替代 MySQL 查询缓存)。
结论
- MySQL 有缓存机制,但 查询缓存(Query Cache)在 MySQL 8.0 已移除,不再推荐使用。
- InnoDB Buffer Pool 是 MySQL 最重要的缓存(缓存表数据和索引),必须合理配置。
- 应用层缓存(如 Redis)是更好的补充方案,适用于热点数据加速。
扩展
问题一:通过什么条件判断 Buffer Pool 中是否存在所需的数据页?
在 MySQL 的 InnoDB 存储引擎中,判断缓冲池(Buffer Pool)中是否存在所需的数据页是通过一个称为页面哈希表(page hash table)的结构来实现。下面是具体的过程和机制:
缓冲池与页面哈希表
缓冲池(Buffer Pool):这是 InnoDB 用于缓存从磁盘读取的数据页和索引页的主要内存区域。它帮助减少从磁盘读取数据的频率,从而提高查询性能。
页面哈希表:为了快速查找缓冲池中的数据页,InnoDB 维护了一个页面哈希表。这个哈希表存储了每个数据页的元数据信息,包括空间 ID(space ID)、页码(page number),以及指向实际数据页在缓冲池中位置的指针。
查找过程
当 MySQL 需要访问某个特定的数据页时,它会根据以下步骤进行查找:
计算哈希值:MySQL 使用空间 ID 和页码作为键,通过哈希函数计算出对应的哈希值。这个哈希值将用于在页面哈希表中定位可能包含所需数据页的位置。
查找哈希表:使用计算得到的哈希值,在页面哈希表中查找是否存在匹配的空间 ID 和页码组合。如果找到了匹配项,则说明所需的页面已经在缓冲池中存在;否则,表示该页面尚未被加载到缓冲池中。
处理未命中情况:如果在页面哈希表中没有找到相应的条目,意味着需要从磁盘加载该页面到缓冲池中。此时,MySQL 会执行一次 I/O 操作,将对应的数据页从磁盘读入缓冲池,并更新页面哈希表以反映这一变化。
返回结果:一旦确定了页面位于缓冲池中(无论是之前就已经存在还是刚刚加载进来的),MySQL 就可以直接访问该页面的内容,而无需再次访问磁盘。
关键点
- 空间 ID 和页码:这两个参数共同唯一标识了一个数据页。空间 ID 对应于数据库表所在的表空间,而页码则是该表空间内的偏移量。
- 锁机制:为了保证并发环境下对缓冲池的安全访问,InnoDB 在管理缓冲池时采用了适当的锁机制。例如,在插入或删除页面哈希表条目时可能会加锁,防止多个线程同时修改相同的资源导致不一致问题。