MySQL 查询缓存
MySQL 查询缓存是查询结果缓存。执行查询语句的时候,会先查询缓存,如果缓存中有对应的查询结果,就会直接返回。
my.cnf
加入以下配置,重启 MySQL 开启查询缓存
query_cache_type=1
query_cache_size=600000
MySQL 执行以下命令也可以开启查询缓存
set global query_cache_type=1;
set global query_cache_size=600000;
查询缓存会在同样的查询条件和数据情况下,直接返回缓存中的结果。但需要注意的是,查询缓存的匹配条件非常严格,任何细微的差异都会导致缓存无法命中。这里的查询条件包括查询语句本身、当前使用的数据库、以及其他可能影响结果的因素,如客户端协议版本号等。
查询缓存不命中的情况:
- 任何两个查询在任何字符上的不同都会导致缓存不命中。
- 如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、MySQL 库中的系统表,其查询结果也不会被缓存。
- 缓存建立之后,MySQL 的查询缓存系统会跟踪查询中涉及的每张表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。
缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做一次缓存操作,失效后还要销毁。 因此,开启查询缓存要谨慎,尤其对于写密集的应用来说更是如此。如果开启,要注意合理控制缓存空间大小,一般来说其大小设置为几十 MB 比较合适。此外,还可以通过 sql_cache
和 sql_no_cache
来控制某个查询语句是否需要缓存:
SELECT sql_no_cache COUNT(*) FROM usr;
MySQL 5.6 开始,查询缓存已默认禁用。MySQL 8.0 开始,已经不再支持查询缓存了
MySQL 8.0:为什么会取消对查询缓存的支持?
尽管 MySQL 查询缓存旨在提高性能,但它存在严重的可扩展性问题,很容易成为严重的瓶颈。
这确实是我们在 MySQL 团队中观察到一段时间的事情。在我们进入今天文章的主题之前,让我先做一个介绍。
查询缓存简介
MySQL 查询缓存是查询结果缓存。它将以 SEL 开头的传入查询与哈希表进行比较,如果存在匹配项,则返回上一次执行查询的结果。有一些限制:
- 查询必须逐字节匹配(查询缓存避免解析)
- 使用非确定性功能将导致查询不被缓存(包括临时表、用户变量、RAND()、NOW() 和 UDF)。
- 查询缓存设计为不提供过时的结果。对基础表的任何修改都会导致这些表的所有缓存失效。
- 对于缓存是否可以用于 InnoDB 有一些限制(为了尊重 MVCC;由于您打开了一个事务,因此“缓存”可能无法表示预期视图中的数据。
最佳情况:
查询缓存的理想方案往往在很大程度上是只读的,其中有许多非常昂贵的查询,这些查询检查数百万行,只返回少数行。一个假设的示例可能是一个复杂的查询,用于为始终显示在网页表单上的下拉列表构建值列表。在这种情况下,查询缓存可以掩盖由缺失索引导致的性能问题,这对新手用户很有帮助。
- 在 MySQL 服务器中,我们现在能够重写查询以插入提示(或其他修改以提高性能)
- 我们有 ProxySQL 等第三方工具,它们可以充当中间人查询缓存。ProxySQL 还支持用于缓存的 TTL,这在我之前提供的示例中工作正常(为下拉列表构建值列表)。
查询缓存的限制
自 MySQL 5.6 (2013) 以来,查询缓存已默认处于禁用状态,因为众所周知,它不会随多核计算机上的高吞吐量工作负载一起扩展。Rene 昨天在他的帖子中证实了这一点,但 Stewart Smith、Domas Mituzas(更新:和 Kristian Koehntopp)之前也提到过这一点。
假设可以提高可伸缩性,则查询缓存的限制因素是,因为只有命中缓存的查询才会得到改进;它不太可能提高性能的可预测性。 对于面向用户的系统,减少性能的可变性通常比提高峰值吞吐量更重要:
决定删除对查询缓存的支持
我们同意密歇根大学安娜堡分校的 Jiamin Huang、Barzan Mozafari、Grant Schoenebeck、Thomas F. Wenisch 进行的研究。我们考虑了我们可以对查询缓存进行哪些改进,以及我们可以进行哪些优化,从而为所有工作负载提供改进。
虽然这些选择本身是正交的,但工程资源是有限的。也就是说,我们正在转变战略,投资于更普遍适用于所有工作负载的改进。
我们也同意 Rene 的结论,即当缓存更靠近 Client 端时,缓存会提供最大的好处