MySQL如何检查缓存命中
MySQL如何检查缓存命中
How MySQL Checks for a Cache Hit
MySQL检查缓存命中的方式相当简单快捷。缓存就是一个查找表(Lookup Table)。查找的键就是查询文本、当前数据库、客户端协议的版本,以及其他少数会影响实际查询结果的因素之哈希值。
在 检查缓存的时候,MySQL不会对语句进行解析、正则化或者参数化,它精确地使用客户端传来的查询语句和其他数据。只要字符大小写、空格或者注释有一点点 不同,查询缓存就认为这是一个不同的查询。这是书写查询语句时要注意的一点。不管怎么说,使用一致的格式和风格是一个好习惯,而且在这种情况下还能得到更 高的性能。
另外一件值得注意的事情就是查询缓存不会存储有不确定结果的查询。因此,任何一个包含不确定函数(比如NOW()或CURRENT_DATE()) 的查询不会被缓存。同样地,CURRENT_USER()或CONNECTION_ID()这些由不同用户执行,将会产生不同的结果的查询也不会被缓存。 事实上,查询缓存不会缓存引用了用户自定义函数、存储函数、用户自定义变量、临时表、mysql数据库中的表或者任何一个有列级权限的表的查询。请参阅 MySQL手册了解所有不会被缓存的查询类型。
经常可以听到"如果查询包含不确定函数,MySQL就不会检查缓存"这样的说法。其实这是不对的。MySQL只有在解析查询的时候才知道里面是否有 不确定函数。缓存查找发生在解析之前。服务器会执行一次不区分大小写的检查来验证查询是否以字母SEL打头。这就是服务器在进行缓存查找前所做的所有事 情。
但是,"如果查询包含NOW()这样的函数,服务器就不会在缓存中找到结果"这种说法是正确的。因为即使服务器在早些时候执行了同样的查询,服务器 也不会有缓存的结果。MySQL一旦发现有阻止缓存的元素存在,它里面就把查询标记为不可缓存,并且产生的结果也不会被保持下来。
对于引用了当天日期的查询,如果想让它被缓存下来,一个有用的技巧就是用一个字面常量来代替函数,比如下面的例子:
... DATE_SUB(CURRENT_DATE, INTERVAL 1 DAY) -- 不可缓存!
... DATE_SUB('2007-07-14', INTERVAL 1 DAY) -- 可缓存
因为查询缓存只针对服务器第一次收到的完整SELECT语句,所以查询里面的子查询或视图不能使用缓存,存储过程中的查询也不能使用缓存。MySQL 5.1之前的准备语句(Prepared Statement)也不能使用缓存。
MySQL查询缓存可以改善性能,但是在使用的时候有一些问题值得注意。首先,开启查询缓存对于读写都增加了某些额外的开销。
读取查询在开始之前必须要检查缓存。
如果查询是可以被缓存的,但是不在缓存中,那么在产生结果之后进行保存会带来一些额外的开销。
最后,写入数据的查询也会有额外的开销,因为它必须使缓存中相关的数据表失效。
这些开销相对来说较小,所以查询缓存还是很有好处的。但是,稍后你会看到,额外的开销有可能也会增加。
对于InnoDB的用户,另外的问题就是事务限制了查询缓存的失效。当事务内部的语句更改了表,即使InnoDB的多版本机制应当对其他语句隐藏事 务的变化,服务器也会使所有引用了该表的查询缓存失效。直到事务提交之前,该表会全局地不可缓存。所以不会有任何引用了该表的查询,不管它是在事务的内部 还是外部,在事务提交之前都能被缓存。因此,长期运行的事务可以增加查询缓存未命中(Cache Miss)的数量。
失效对于大型查询缓存也会是一个问题。如果缓存中有许多查询,缓存失效就会需要很长的时间并且延缓整个系统的工作。这是因为查询缓存有一个全局锁,它会阻塞所有访问缓存的查询。在检查查询是否命中,以及是否有查询失效的时候都会发生访问动作。