MySQL 缓存机制就是缓存 sql 文本及缓存结果,用 KV 形式保存再服务器内存中,如果运行相同的 sql,服务器直接从缓存中去获取结果,不需要在再去解析、优化、执行 sql。
如果这个表修改了,那么使用这个表中的所有缓存将不再有效,查询缓存值得相关条目将被清空。表中得任何改变是值表中任何数据或者是结构的改变,包括 insert, update, delete, truncate, alter table, drop table 或者是 drop database 包括那些映射到改变了的表的使用 merge 表的查询,显然,对于频繁更新的表,查询缓存不合适,对于一些不变的数据且有大量相同 sql 查询的表,查询缓存会节省很大的性能。
缓存存在一个 hash 表中,通过查询 SQL,查询数据库,客户端协议等作为 key,在判断命中前,mysql 不会解析 SQL,而是使用 SQL 去查询缓存,SQL 上的任何字符的不同,如空格,注释,都会导致缓存不命中。如果查询有不确定的数据 like now(),current_date(),那么查询完成后结果者不会被缓存,包含不确定的数的是不会放置到缓存中。
下面是关于 Query Cache 相关参数:
MySQL 的查询缓存有如下特点:
完全相同的 SQL 才会命中缓存
在查询缓存中搜索结果前,MySQL 不会对 SQL 进行解析,因此,查询缓存的查找方式是字节匹配。也就是说,如果 SQL 中包含不确定内容、多余的空格都会被认为是不同的 SQL。
当服务器端接收到查询包后,系统就会检查查询缓存中是否有该查询,因此利用查询缓存可以省去 SQL 解析和处理操作。MySQL 得到结果集后,将结果集以包的形式发送到客户端,在将包发送到客户端之前会将包保存到查询缓存中。是否将结果集插入到查询缓存取决于查询 SQL,能够插入到查询缓存的对象如下第 2 点所示。
MySQL的缓存对象如下:
只缓存 SELECT 语句。SHOW 命令和存储程序不会被缓存。
不能缓存预编译语句(prepared statement)和游标。 查询缓存中保存的是查询语句和结果集,而预编译语句中存在替代符和额外的参数,游标从块中读取结果。
查询语句不能包含动态内容。多次执行某 SQL,必须能够返回相同的结果集,因此查询中不能包含像 UUID(), RAND(), CONNECTION_ID() 这样的函数。
SQL 中包含定义函数和自定义变量不会被缓存。
对系统表的查询不会被缓存。
非自动提交(显示使用 BEGIN…END)事务中的 SQL 不会被缓存。
使用 TEMPORARY 表的 SQL 不会被缓存。
不使用任何表的 SQL 不会被缓存。
在下面的 SELECT 操作也不会被缓存:
SELECT …IN SHARE MODE SELECT …FOR UPDATE SELECT …INTO OUTFILE … SELECT …INTO DUMPFILE … SELECT * FROM …WHERE autoincrement_col IS NULL
表内容更改时缓存失效
修改表的所有操作(DELETE/INSERT/UPDATE 等等),都会导致缓存中该表的所有内容(SQL 和结果集)被一次清空。很有可能这些操作并没有改变 SQL 的结果集,但 MySQL 无法验证哪些 SQL 会影响缓存而哪些 SQL 不会影响。也是由于这点,MySQL 的缓存利用率不是很高。对于写操作频繁的应用,查询缓存的命中率会相当的低。如果缓存中存在某表的大量 SQL,多少也会降低该表的更新速度。
缓存碎片
随着缓存量的增多,查询缓存会产生碎片,这将降低缓存性能。状态变量 Qcache_free_blocks 描述了缓存中的空闲块,该值越大表示碎片越多。可以用 FLUSH QUERY CACHE 命令来整理碎片,而对于大缓存,该操作会长时间阻塞查询缓存。
查询缓存的优缺点
- 不需要对 SQL 语句做任何解析和执行,当然语法解析必须通过先,直接从 query cache 中获取查询结果;
- 查询缓存的判断规则不够智能,也提高了查询缓存的使用门槛,降低其效率;
- query cache 的启用,会增加检查和清理 query cache 中记录集的开销,而且存在 SQL 语句缓存的表,每一张都只有一个对应的全局锁。
查询缓存不适用场景
- 子查询或者外层查询;
- 存储过程、存储函数、触发器、event 中调用的 SQL,或者引用到这些结果的;
- 包含一些特殊函数时,例如:BENCHMARK()、CURDATE()、CURRENT_TIMESTAMP()、NOW()、RAND()、UUID() 等等;
- 读取 mysql、INFORMATION_SCHEMA、performance_schema 库数据的;
- 类似 SELECT…LOCK IN SHARE MODE、SELECT…FOR UPDATE、SELECT…INTO OUTFILE/DUMPFILE、SELECT…WHRE…IS NULL 等语句;
- SELECT 执行计划用到临时表(TEMPORARY TABLE);
- 未引用任何表的查询,例如 SELECT 1+1 这种;
- 产生了 warnings 的查询;
- SELECT 语句里加了 SQL_NO_CACHE 关键字;
总结
- MySQL 的查询缓存利用率很低,原因是每当有修改表内容操作时,缓存中所有与该表相关的内容全部要被清空。
- 查询缓存是一次申请 query_cache_size 大小的内存,而不是随查询的插入动态申请,这样提升了系统性能,因为申请、释放内存的操作很慢。
- 查询缓存的空闲块是有序的,因为较小的块会被经常使用,同样为了性能考虑。
- 为充分利用内存,某缓存块填充数据后,如果还有空闲空间,则将空闲空间回收。
- 缓存替换策略是,当缓存没有空闲块时,系统将最老(最近没有被使用)的查询块剔除。