前言

Although MySQL Query Cache was meant to improve performance, it has serious scalability issues and it can easily become a severe bottleneck.

虽然MySQL查询缓存是为了提高性能,但它有严重的可扩展性问题,很容易成为一个严重的瓶颈。——MySQL Official Blog

MySQL在其最新的8.0版本中,删除了查询缓存(Query Cache)区域,就此,MySQL的Query Cache彻底的退出了历史舞台。在5.7版本中,MySQL已经将Query Cache的选项(query_cache_type)的缺省值设为了关闭,并在5.7.20版本中,将该配置标记为了Deprecated

那么MySQL经历这么多个版本的迭代后,为什么会取消该区域?Query Cache设计的初衷是什么?

本篇,让我们来一起了解MySQL Query Cache

什么是MySQL Query Cache

The query cache stores the text of a SELECT statement together with the corresponding result that was sent to the client. If an identical statement is received later, the server retrieves the results from the query cache rather than parsing and executing the statement again. The query cache is shared among sessions, so a result set generated by one client can be sent in response to the same query issued by another client.

The query cache can be useful in an environment where you have tables that do not change very often and for which the server receives many identical queries.

查询缓存存储了SELECT语句的文本,以及发送给客户端的相应结果。如果后来收到一个相同的语句,服务器会从查询缓存中检索结果,而不是再次解析和执行该语句。查询缓存是在会话之间共享的,因此一个客户端产生的结果集可以被用来响应另一个客户端发出的相同查询。

查询缓存在这样的环境中很有用,即你有一些不经常变化的表,而服务器收到许多相同的查询。

以上是MySQL官方文档中对于Query Cache的定义,MySQL查询缓存是MySQL中比较独特的一个缓存区域,用来缓存特定Query的整个结果集信息,且共享给所有客户端。

为了提高完全相同的Query语句的响应速度,MySQL 会对查询语句进行hash计算后,把得到的hash值与Query查询的结果集对应存放在Query Cache中。

当开启Query Cache之后,MySQL 会对接收到的每一个SELECT语句通过特定的hash算法计算该Queryhash值,然后通过该hash值到Query Cache中去匹配。

如果通过hash值匹配到了一样的Query,则直接将cache中相应的Query结果集返回给客户端。

由于Query Cache是针对SELECT语句的hash值作为key值进行存储的,意味着SQL语句哪怕出现一个字符的不同,缓存也无法进行命中。

对于5.7.20版本之前,可以通过编辑my.cnf配置,通过如下参数开启Query Cache

...[mysqld]query_cache_type=1query_cache_size=10Mquery_cache_limit=256K

各个参数含义如下:

参数含义取值范围
query_cache_type查询缓存类型0:关闭缓存
1:缓存全部请求,仅当语句中包含SELECT SQL_NO_CACHE时放弃缓存
2:对指定的请求进行缓存,仅当语句中包含SELECT SQL_CACHE时进行缓存
默认关闭
query_cache_size查询缓存区大小默认大小1M,该值必须为1024的整数倍,如果不是整数倍,MySQL 会自动调整降低最小量以达到1024的倍数
query_cache_limit缓存结果集大小限制,允许 Cache 的单条 Query 结果集的最大容量,如果超过该值,则不会缓存结果集默认大小1MB

Query Cache的优势

Query Cache设计之初,MySQL希望可以利用查询缓存,提升查询的效率,在执行每一次SELECT的时候,MySQL都会首先经过Query Cache区域,检查查询是否可以命中缓存,如果命中,则直接返回结果集,相较于从硬盘(Disk)检索数据,直接从内存(RAM)中获取数据集无疑是极为高效的,可以大大的节省查询执行的时间。

Searches for a single row in a single-row table are 238% faster with the query cache than without it. This can be regarded as close to the minimum speedup to be expected for a query that is cached.

——MySQL Docs

如果业务场景是只读不写的情况,且有大量重复的查询请求,开启Query Cache会带来巨大的性能提升,这意味着每一次查询请求,会有很大的概率直接命中缓存,而无需经过SQL解析优化,硬盘加载数据等一系列过程。

Query Cache的劣势

1、查询SQL的命中

Query Cache中对SQL语句的缓存,是基于字节级别的(The query must match byte-for-byte ),这意味着SQL语句发生任何一点变化,Query Cache都无法进行命中,这对于实际生产环境的查询,显得有些过于苛刻。

2、缓存过期

The query cache was designed to not serve stale results. Any modification to the underlying table(s) results in all cache being invalidated for those tables.

Query Cache的淘汰策略过于苛刻,任何对于表中数据的修改,都会使得缓存失效,这里的修改包括:INSERT, UPDATE, DELETE, TRUNCATE, ALTER TABLE, DROP TABLE, or DROP DATABASE,这个特性意味着,只有对于读远大于写的数据表,Query Cache才能发挥作用,对于读写均衡以及写多读少的场景,Query Cache基本上很难发挥作用。

3、分区表禁用

The query cache is not supported for partitioned tables, and is automatically disabled for queries involving partitioned tables. The query cache cannot be enabled for such queries.

如果数据表使用了分区,Query Cache将会被自动的禁用,无法生效。

4、增加额外的负载

If all the queries you are performing are simple (such as selecting a row from a table with one row), but still differ so that the queries cannot be cached, the overhead for having the query cache active is 13%

当开启Query Cache选项后,如果查询请求没有命中Query Cache时,MySQL会需要额外的性能开销去处理结果集,写入Query Cache中,最糟糕的情况下,这个额外的性能开销是13%,但实际场景中的情况会更加的复杂,通常情况下,额外的性能开销会低于该值,但这仍是一笔无谓的性能损耗。

总上,我们会发现,Query Cache好像非常的鸡肋,因为在我们大多数的场景中,很少情况下会出现只读不写的情况,更多的情况则是读多写少或读写均衡,这使得Query Cache很难对我们的实际业务产生正向的影响。

MySQL官方的抉择

经过上述对Query Cache的优缺点的了解,我们可以明白Query Cache适合于什么样的业务场景,但对于大多数场景下,Query Cache是比较鸡肋的,因此,从MySQL 5.6版本开始,将Query Cache设置为了默认关闭,并在MySQL 8.0版本中,彻底移除了Query Cache,并给出了解释:

Assuming that scalability could be improved, the limiting factor of the query cache is that since only queries that hit the cache will see improvement; it is unlikely to improve predictability of performance. For user facing systems, reducing the variability of performance is often more important than improving peak throughput.

假设可扩展性可以得到改善,那么查询缓存的限制因素是,由于只有击中缓存的查询才能看到改善;它不太可能改善性能的可预测性。

对于面向用户的系统来说,减少性能的可变性往往比提高峰值吞吐量更重要。

We considered what improvements we could make to query cache versus optimizations that we could make which provide improvements to all workloads.

While these choices themselves are orthogonal, engineering resources are finite. That is to say that we are shifting strategy to invest in improvements that are more generally applicable to all workloads.

我们考虑了对查询缓存的改进和对所有工作负载的优化,我们可以做哪些改进。

虽然这些选择本身是正交的,但工程资源是有限的。

这就是说,我们正在转变策略,投入于更普遍适用于所有工作负载的改进。

MySQL官方团队对于在8.0版本中彻底移除Query Cache的决策做出了如上的解释,并给出了所替代的解决方案建议——使用第三方工具客户端缓存ProxySQL 来代替Query Cache

如下图所示,MySQL官方给出了使用ProxySQL 对比原生Query Cache 性能报告,从图中可以清晰的看到,ProxySQL 的查询性能完胜原生的Query Cache

对于ProxySQL的使用,在这里将不会过多的介绍,在后面的篇幅中,我们会详细聊一聊ProxySQL,如果您对ProxySQL感兴趣,可以参考其官方文档:

Query Cache

本篇参考:

MySQL 8.0: Retiring Support for the Query Cache:

https://dev.mysql.com/blog-archive/mysql-8-0-retiring-support-for-the-query-cache/

The MySQL Query Cache:

https://dev.mysql.com/doc/refman/5.7/en/query-cache.html

How To Optimize MySQL with Query Cache on Ubuntu 18.04:

https://www.digitalocean.com/community/tutorials/how-to-optimize-mysql-with-query-cache-on-ubuntu-18-04