缓存存放在一个应用表中,通过一个哈希值应用,这个哈希值包括了如下因素,查询本身,当前要查询的数据库,客户端协议的版本等一些其他可能会影响返回结果的信息。
当判断缓存是否命中时,Mysql不会解析,“正规化”或者参数化查询语句,而是直接适用SQL语句和客户端发送过来的其他原始信息。任何字符上的不同,例如空额,注释等都会导致缓存的不命中,所以统一的编码规则是一个好的习惯。
检查缓存的时候并没有解析SQL语句,所以Mysql并不知道查询语句中是否包含不确定函数。在检查缓存之前,Mysql就做一件事情,就是通过一个大小写不敏感的检查看看SQL是不是以SEL开头。
不过也有一些问题需要注意。首先,打开查询缓存对读和写操作都会带来额外的消耗:
读查询在开始之前必须先检查是否命中缓存
如果这个读查询可以被缓存,那么当执行后,Mysql若发现查询缓存没有这个查询,会将其结果存入查询缓存,带来额外的系统消耗
这对写操作也会有影响,因为当向某个表写入数据的时候,Mysql必须将对应表的所有缓存都设置失效。如果查询缓存非常大或者碎片很多,这个操作可能带来很大的系统消耗。
虽然如此,查询缓存仍然可能给系统带来性能提升。但是,如上所述,这些额外消耗也可能不断增加,再加上对查询缓存操作是一个加锁排他操作,这个消耗可能不容小觑。
对InnoDB用户来说,事务的一些特性会限制查询缓存的使用。当一个语句在事务中修改了某个表,mysql会将这个表的 对应查询缓存都设置失效。而事实上,InnoDB的多版本特性会展示将这个修改对其他事务屏蔽。在这个事务提交之前,这个表的相关查询是无法被缓存的。所以所有在这个表上的查询——内部或者外部的事务——都只能在该事务提交后才被缓存。因此,长时间运行的事务,会大大降低查询缓存的命中率。
缓存不命中的原因:
查询语句无法被缓存,可能因为查询中包含一个不确定的函数,或者查询结果太大无法缓存。
Mysql从未处理这个查询,所以之前没缓存过
之前缓存了查询结果,但是由于查询缓存的内存用完了,Mysql需要将某些缓存逐出
数据表修改导致缓存失效
如果有大量缓存没命中,但是实际缓存了,那么原因一定如下:
查询缓存没有完成余热。Mysql没有机会将查询结果都缓存起来。
查询语句之前从未执行过。
查询语句之前从未执行过。缓存失效操作太多了。
配置和维护缓存:
query_cache_type
OFF,ON或者DEMAND,DEMAND表示只有在查询语句中明确写明了SQL_CACHE的语句才会放入查询缓存。
query_cache-size
查询缓存使用总空间,单位字节。这个值必须是1024的整数倍,否则Mysql实际分配的数据回合你指定的不同。
query_cache_min_res_unit
在查询缓存中分配内存块时最小单位。
query_cache_limit
mysql能够缓存的最大查询结果。只有当结果全部返回后,Mysql才知道查询结果是否超出限制。
query_cache_wlock_invalidate
如果某个数据表被其他的链接锁住,是否仍然从查询缓存中返回结果。默认为off。
InnoDB和查询缓存
因为InnoDB有自己的MVCC机智,所以相比其他存储过程,InnoDB和查询缓存交换要更加复杂。
关于MVCC:http://www.cnblogs.com/chenpingzhao/p/5065316.html
通用查询缓存优化:
多个小表代替一个大表可以使实效策略能够在一个更合适的粒度上进行。当然,过犹不及。
批量写入时时只要做一次缓存失效,所以相比单挑写入效率更好。(PS:不要同时做延迟写和批量写,否则可能会因为失效导致服务器僵死较长时间。)
因为缓存空间太大,在国企操作的时候可能导致服务器僵死。解决方案:控制缓存空间的大小。
无法在数据库或者表级别控制查询语句缓存,但是可以通过sql_cache和sql_no_cache来控制某个select语句是否需要进行缓存。
对于写密集型的应用来说,直接禁用查询缓存可能会提高系统的性能。关闭查询缓存可以移除所有相关消耗。
因为对互斥信号量的竞争,有时直接关闭查询缓存对读密集型的应用也会有好处。
关于缓存优化,可以做一个相关测试,对比打开和关闭查询缓存时候的性能差异。