Jamzy Wang

life is a struggle,be willing to do,be happy to bear~~~

MySQL查询缓存小结

2014-07-29 21:32

原创声明:本作品采用知识共享署名-非商业性使用 3.0 版本许可协议进行许可,欢迎转载,演绎,但是必须保留本文的署名(包含链接),且不得用于商业目的。

与Oracle不同,MySQL的查询缓存并非缓存执行计划,而是查询及其结果集,这就意味着只有相同的查询操作才能命中缓存,因此MySQL的查询缓存命中率很低,另一方面,对于大结果集的查询,其查询结果可以从cache中直接读取,有效的提升了查询效率。

MySQL的查询缓存有如下特点:

(1)完全相同的SQL才会命中缓存。

在查询缓存中搜索结果前,MySQL不会对SQL进行解析,因此,查询缓存的查找方式是字节匹配。也就是说,如果SQL中包含不确定内容、多余的空格都会被认为是不同的SQL。

当服务器端接收到查询包后,系统就会检查查询缓存中是否有该查询,因此利用查询缓存可以省去SQL解析和处理操作。MySQL得到结果集后,将结果集以包的形式发送到客户端,在将包发送到客户端之前会将包保存到查询缓存中。是否将结果集插入到查询缓存取决于查询SQL,能够插入到查询缓存的对象如下第(2)点所示。

(2)MySQL的缓存对象如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a	只缓存SELECT语句。SHOW命令和存储程序不会被缓存。
b	不能缓存预编译语句(prepared statement)和游标。
查询缓存中保存的是查询语句和结果集,而预编译语句中存在替代符和额外的参数,游标从块中读取结果
c	查询语句不能包含动态内容。多次执行某SQL,必须能够返回相同的结果集,因此查询中不能包含像UUID(), RAND(), CONNECTION_ID()这样的函数。
d	SQL中包含定义函数和自定义变量不会被缓存。
e	对系统表的查询不会被缓存。
f	非自动提交(显示使用BEGINEND)事务中的SQL不会被缓存。
g	使用TEMPORARY表的SQL不会被缓存。
h	不使用任何表的SQL不会被缓存。
i	在下面的SELECT操作也不会被缓存:

SELECT ...IN SHARE MODE
SELECT ...FOR UPDATE
SELECT ...INTO OUTFILE ...
SELECT ...INTO DUMPFILE ...
SELECT * FROM ...WHERE autoincrement_col IS NULL

(3)表内容更改时缓存失效。

修改表的所有操作(DELETE/INSERT/UPDATE等等),都会导致缓存中该表的所有内容(SQL和结果集)被一次清空。很有可能这些操作并没有改变SQL的结果集,但MySQL无法验证哪些SQL会影响缓存而哪些SQL不会影响。也是由于这点,MySQL的缓存利用率不是很高。对于写操作频繁的应用,查询缓存的命中率会相当的低。如果缓存中存在某表的大量SQL,多少也会降低该表的更新速度。

(4)缓存碎片。

随着缓存量的增多,查询缓存会产生碎片,这将降低缓存性能。状态变量Qcache_free_blocks描述了缓存中的空闲块,该值越大表示碎片越多。可以用FLUSH QUERY CACHE命令来整理碎片,而对于大缓存,该操作会长时间阻塞查询缓存。

总结一下:

1
2
3
4
5
1MySQL的查询缓存利用率很低,原因是每当有修改表内容操作时,缓存中所有与该表相关的内容全部要被清空。
2)查询缓存是一次申请query_cache_size大小的内存,而不是随查询的插入动态申请,这样提升了系统性能,因为申请、释放内存的操作很慢。
3)查询缓存的空闲块是有序的,因为较小的块会被经常使用,同样为了性能考虑。
4)为充分利用内存,某缓存块填充数据后,如果还有空闲空间,则将空闲空间回收。
5)缓存替换策略是,当缓存没有空闲块时,系统将最老(最近没有被使用)的查询块剔除。

Comments