vlambda博客
学习文章列表

使用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


---------

往期推荐


~~~~~~~~~~~~~

一起推进电商业务信息化