查询性能优化的关键之一
什么是查询缓存
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语句是否需要进行缓存
- 对于 写密集型的应用来说,直接禁用查询缓存可能会提高系统的性能
- 因为对互斥信号量的竞争,有时直接关闭查询缓存对读密集型的应用也会有好处