8.10.4 缓存准备好的语句和存储的程序

对于 Client 端可能在会话中多次执行的某些语句,服务器会将语句转换为内部结构,并缓存该结构以在执行期间使用。缓存使服务器能够更有效地执行,因为它避免了会话期间再次需要重新转换语句的开销。这些语句发生转换和缓存:

  • 准备好的语句,包括在 SQL 级别处理的语句(使用PREPARE语句)和使用二进制 Client 端/服务器协议处理的语句(使用mysql_stmt_prepare() C API 函数)。 max_prepared_stmt_count系统变量控制服务器缓存的语句总数。 (所有会话中准备好的语句总数)。

  • 存储的程序(存储的过程和功能,触发器和事件)。在这种情况下,服务器将转换并缓存整个程序主体。 stored_program_cache系统变量指示服务器每个会话缓存的存储程序的大概数量。

服务器在每个会话的基础上为准备好的语句和存储的程序维护高速缓存。为一个会话缓存的语句无法被其他会话访问。会话结束时,服务器将丢弃为其缓存的所有语句。

当服务器使用缓存的内部语句结构时,必须注意该结构不会过时。语句所使用的对象可能发生元数据更改,从而导致当前对象定义与内部语句结构中 table 示的定义不匹配。 DDL 语句会发生元数据更改,例如那些创建,删除,更改,重命名或截断 table,或分析,优化或修复 table 的语句。table 内容的更改(例如,使用INSERTUPDATE)不会更改元数据,也不会更改SELECT语句。

这是问题的例证。假设 Client 准备以下语句:

PREPARE s1 FROM 'SELECT * FROM t1';

SELECT *在内部结构中扩展到 table 中的列列 table。如果 table 中的列集被ALTER TABLE修改,则准备好的语句已过期。如果服务器在下次 Client 端执行s1时未检测到此更改,则准备好的语句将返回错误的结果。

为了避免由准备好的语句引用的 table 或视图的元数据更改引起的问题,服务器将检测到这些更改,并在下次执行该语句时自动重新准备该语句。即,服务器重新解析该语句并重建内部结构。从 table 定义高速缓存中清除引用的 table 或视图之后,也会重新进行解析,这是隐式为高速缓存中的新条目腾出空间,还是由于FLUSH TABLES而显式地腾出空间。

同样,如果存储程序使用的对象发生更改,则服务器将重新解析程序中受影响的语句。

服务器还检测 table 达式中对象的元数据更改。这些可能用于特定于存储程序的语句(例如DECLARE CURSOR)或流控制语句(例如IFCASERETURN)。

为了避免重新解析整个存储的程序,服务器仅在需要时才重新解析程序中受影响的语句或 table 达式。例子:

  • 假设 table 或视图的元数据已更改。对于访问 table 或视图的程序中的SELECT *,将进行重新解析,但对于不访问 table 或视图的SELECT *,则不会进行重新解析。

  • 当一条语句受到影响时,服务器将在可能的情况下仅部分对其进行重新分析。考虑以下CASE语句:

CASE case_expr
  WHEN when_expr1 ...
  WHEN when_expr2 ...
  WHEN when_expr3 ...
  ...
END CASE

如果元数据更改仅影响WHEN when_expr3,则将重新解析该 table 达式。 * case_expr *和其他WHENtable 达式不被解析。

重新解析使用默认的数据库和 SQL 模式,该模式对原始转换为内部格式有效。

服务器尝试最多进行三次重新解析。如果所有尝试均失败,则会发生错误。

重新解析是自动的,但是在一定程度上会降低准备好的语句和存储程序的性能。

对于准备好的语句,状态变量Com_stmt_reprepare跟踪重新准备的次数。