MySQL优化之查询缓存

查询性能优化的关键之一

什么是查询缓存

MySQL查询缓存保存查询返回的完整结果,当查询命中该缓存,MySQL会立刻返回结果,跳过了解析、优化和执行阶段

缓存命中

MySQL判断缓存命中的方法很简单,缓存放在一个引用表中,通过一个哈希值引用,这个哈希值包括了如下因素:

  • 查询本身
  • 当前要查询的数据库
  • 客户端协议的版本等一些其他可能会影响返回结果的信息

当判断缓存是否命中时,MySQL不会解析“正规化”或者参数化查询语句,而是直接使用SQL语句和客户端发送过来的其他原始信息。任何字符上的不同,例如空格、注释——都会导致缓存的不命中。

未命中的可能情况

  • 由于查询语句中包含不确定的函数,或者查询结果太大,超过query_cache_limit的值,查询语句无法被缓存
  • 查询语句之前从未执行过,查询结果没有缓存过
  • 之前缓存了查询结果,但是由于查询缓存内存不足,MySQL将某些缓存逐出,导致未命中
  • 查询缓存还没有完成预热,MySQL还没有机会将查询结果都缓存起来
  • 缓存失效操作太多了,数据修改,内存不足,缓存碎片都会导致缓存失效

不会缓存结构的情况

  • 当查询语句中有一些不确定的数据时,则不会被缓存,例如包含函数NOW()或者CURRENT_DATE()的查询不会被缓存,只要包含任何用户自定义函数、存储函数、用户变量、临时表、mysql库中的系统表,或者任何包含列级别权限的表,都不会被缓存
  • 当查询的结果大于query_cache_limit设置的值时,结果不会被缓存。
  • 对于InnoDB引擎来说,当一个语句在事务中修改了某个表,那么在这个事务提交之前,所有与这个表相关的查询都无法被缓存。因此长时间执行事务,会大大降低缓存命中率。

额外消耗

打开查询缓存对读和写操作都会带来额外的消耗:

  • 读查询在开始之前必须先检查是否命中缓存
  • 如果这个读查询可以被缓存,那么当完成执行后,MySQL若发现查询缓存中没有这个查询,会将其结果存入查询缓存,这会带来额外的系统消耗
  • 当向某个表写入数据的时候,MySQL必须将对应表的所有缓存都设置失效,如果查询缓存非常大或者碎片很多,这个操作就可能会带来很大系统消耗

但对于需要消耗大量资源的查询通常都是非常适合缓存的。

缓存参数配置:

  • query_cache_type,是否打开查询缓存
  • query_cache_size,查询缓存使用的总内存空间
  • query_cache_min_res_unit,在查询缓存中分配内存块时的最小单位,可以帮助减少由碎片导致的内存空间浪费
  • query_cache_limit,MySQL能够缓存的最大查询结果
  • query_cache_wlock_invalidate,如果某个数据表被其他的连接锁住,是否仍然从查询缓存中返回结果

InnoDB和查询缓存

  • 事务是否可以访问查询缓存取决于当前事务ID,以及对应的数据表上是否有锁
  • 如果表上有任何的锁,那么对这个表的任何查询语句都是无法被缓存的

通用查询缓存优化:

  • 用多个小表代替一个大表对查询缓存有好处
  • 批量写入时只需要做一次缓存失效,所以相比单条写入效率更好
  • 因为缓存空间太大,在过期操作的时候可能会导致服务器僵死,控制缓存空间的大小
  • 无法在数据库或者表级别控制查询缓存,但是可以通过SQL_CACHE和SQL_NO_CACHE来控制某个SELECT语句是否需要进行缓存
  • 对于 写密集型的应用来说,直接禁用查询缓存可能会提高系统的性能
  • 因为对互斥信号量的竞争,有时直接关闭查询缓存对读密集型的应用也会有好处