使用Query Cache,MySQL查询从0.38秒降到0.11秒
“缓存是一个好东西,以前做过的事,咱不用再重复了。”
工作中一个典型场景
老板:咱们网站慢的要死, 怎么会这样,Web应用的机器挺好的, 咋还有这样的问题?
码农:呃…… Web服务器是挺好的, 现在慢的是数据库服务器。
老板:什么?以前你跟我说, MySQL很好了, 不用买Oracle,现在你又跟我MySQL不行了。怎么能这样?
码农:呃…… Web服务器性能好, 给数据库带来了大量查询,现在数据为服务器处理不完这些查询了。只有一个数据库服务器, 来应对Web服务器的请求
老板:现在这个节骨眼儿上, 也不能买Oracle了, 你说,现在咋办?
老板的老板(老板想像的画面):现在这个项目从开头就是一个灾难性的失败,咱们已经搞了一个新的数据服务器, 你告诉我要延期了, 要不还得花更多的钱。你认为咱们是印钱的么?我让别人来做这个事吧。
同事(即将接手这个难题):等下, 觉得我可以搞定这个事。
针对上面的问题,你同事比你多知道些什么呢?他怎么帮老板找会面子?上面场景中, 造成问题的原因可能很多, 也会有多个解决方式。我们可以尝试优化查询和索引, 升级硬件,针对MySQL调参优化, 也有另外的方式,如主从复制后,读写分离。不过,MySQL还有另外一个快速上手解决问题的功能:Query Cache。某种业务场景下, 可能会针对同样的数据,重复地执行两样的SQL查询,且每次都返回同样的结果,MySQL可以缓存下这些数据,从而避免再从数据文件中读取的开销。
设置Query Cache
为了确保MySQL能使用到Query Cache, 有许多变量需要设置。第一个是query_cache_type。针对这个变量有三个可能的值:0 (代表关掉, 不使用), 1(代表开启,使用缓存)和2(代表随需开启,后面会详细讨论)。一般情况下, 我们把
query-cache-type = 1
放到MySQL的配置文件中。重启MySQL服务器后, 执行下面的查询,
mysql> SHOW VARIABLES LIKE '%query_cache%';
+-------------------+---------+
| Variable_name | Value |
+-------------------+---------+
| have_query_cache | YES |
| query_cache_limit | 1048576 |
| query_cache_size | 0 |
| query_cache_type | ON |
+-------------------+---------+
不过,还有其它的参数需要设置, query_cache_size。 如果这个变量设置成0(默认是0)的话,缓存也还是没有打开。这个变量控制着缓存使用内存的大小, 这里我们设置成20MB。
query-cache-size = 20M
重启后观察效果:
mysql> SHOW VARIABLES LIKE '%query_cache%';
+-------------------+----------+
| Variable_name | Value |
+-------------------+----------+
| have_query_cache | YES |
| query_cache_limit | 1048576 |
| query_cache_size | 20971520 |
| query_cache_type | ON |
+-------------------+----------+
Query Cache实操
本例子中, 我使用了这份数据(大家可以从这里下载:http://download.wikipedia.org/)。使用的机器也很慢, 其它也没有做什么调整,以减少对结果集的干扰。下面我们针对同样的SQL执行两次,看看第二次时的改进情况。
SELECT * FROM cur;
...
14144 rows in set (2.96 sec)
执行第二次:
SELECT * FROM cur;
14144 rows in set (3.02 sec)
再执行一次:
SELECT * FROM cur;
14144 rows in set (3.02 sec)
什么情况?是期望第二次执行时,用更少的时间,不过没有看到明显减少的耗时。下面检查下, 还有哪些参数没有配置好,导致没有预期的效果。
mysql> SHOW STATUS LIKE '%qcache%';
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Qcache_queries_in_cache | 0 |
| Qcache_inserts | 2 |
| Qcache_hits | 0 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 2 |
| Qcache_free_memory | 20962720 |
| Qcache_free_blocks | 1 |
| Qcache_total_blocks | 1 |
+-------------------------+----------+
上面两条查询都记录了(对应着Qcache_inserts的值是2), 不过两次的结果都没有存下来(如果你实验时又运行别的SQL的话,结果会不一样)。细看后,发现没有缓存结果集的原因是返回的结果集太大了。实验中我用了Wikipedia Esperanto dump数据,超过了默认缓存空间的限制。这里有两个限制在起作用:query_cache_limit参数决定了单次查询结果集的大小能不能入缓存(这个变量的默认值是1M),另一个是query_cache_size,决定了总缓存的大小。这里造成没有入缓存的是query_cache_limit。这里查询的结果集大于1M了,最终没有入缓存。
知道原因后, 我们调整也查询, 使用下面的SQL:
SELECT cur_is_new FROM cur WHERE cur_user_text > 'Y'
2336 rows in set (0.38 sec)
再看下缓存情况:
mysql> SHOW STATUS LIKE '%qcache%';
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Qcache_queries_in_cache | 1 |
| Qcache_inserts | 3 |
| Qcache_hits | 0 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 2 |
| Qcache_free_memory | 20947592 |
| Qcache_free_blocks | 1 |
| Qcache_total_blocks | 4 |
+-------------------------+----------+
看到上面的Qcache_queries_in_cache值是1,也就是说结果集入缓存了。这个SQL第一次执行,耗时0.38秒,我们再看下第二次执行时,结果怎样:
SELECT cur_is_new FROM cur WHERE cur_user_text > 'Y'
2336 rows in set (0.11 sec)
耗时从第一次的0.38秒,降到0.11秒,很不错了。
看下缓存方面的统计情况:
mysql> SHOW STATUS LIKE '%qcache%';
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Qcache_queries_in_cache | 1 |
| Qcache_inserts | 3 |
| Qcache_hits | 1 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 2 |
| Qcache_free_memory | 20947592 |
| Qcache_free_blocks | 1 |
| Qcache_total_blocks | 4 |
+-------------------------+----------+
缓存命中一次(变量Qcache_hits值是1)。也证实了上面咱们的理论解释。
本文译自:https://www.databasejournal.com/features/mysql/article.php/3110171/MySQLs-Query-Cache.htm
---------
往期推荐
~~~~~~~~~~~~~
一起推进电商业务信息化