Skip to main content

3.预准备语句和存储的程序的缓存

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

  • 预准备的语句,包括在 SQL 级别处理的语句(使用 PREPARE 语句)和使用二进制客户端/服务器协议处理的语句(使用 mysql_stmt_prepare() C API 函数)。 max_prepared_stmt_count 系统变量控制服务器缓存的语句总数。 (所有会话中准备好的语句数量的总和。)
  • 存储程序(存储过程和函数、触发器和事件)。 在这种情况下,服务器会转换并缓存整个程序主体。 stored_program_cache 系统变量指示服务器每个会话缓存的存储程序的大致数量。

服务器为每个会话维护准备好的语句和存储的程序的缓存。 为一个会话缓存的语句不可被其他会话访问。 当会话结束时,服务器会丢弃为其缓存的所有语句。

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

这是问题的一个说明。 假设客户端准备以下声明:

PREPARE s1 FROM 'SELECT * FROM t1';

SELECT * 在内部结构中扩展到表中的列列表。 如果使用 ALTER TABLE 修改表中的列集,则预准备语句就会过时。 如果客户端下次执行 s1 时服务器没有检测到此更改,则准备好的语句将返回错误的结果。

为了避免因预准备语句引用的表或视图的元数据更改而导致问题,服务器会检测这些更改并在下次执行该语句时自动重新准备该语句。 即服务器重新解析语句并重建内部结构。 在从表定义缓存中刷新引用的表或视图之后,也会发生重新解析,或者隐式地为缓存中的新条目腾出空间,或者显式地由于 FLUSH TABLES 而进行。

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

服务器还检测表达式中对象的元数据更改。 这些可以用在特定于存储程序的语句中,例如 DECLARE CURSOR 或流控制语句(例如 IF、CASE 和 RETURN)。

为了避免重新分析整个存储的程序,服务器仅根据需要重新分析程序中受影响的语句或表达式。 例子:

  • 假设表或视图的元数据发生更改。 重新分析发生在程序内访问表或视图的 SELECT * 上,但不会发生在不访问表或视图的 SELECT * 上。

  • 当一条语句受到影响时,如果可能,服务器只会部分地重新解析它。 考虑这个 CASE 语句:

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

    如果元数据更改仅影响何时when_expr3,则重新解析该表达式。 case_expr 和其他 WHEN 表达式不会被重新分析。

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

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

重新分析是自动的,但如果发生的话,会降低准备好的语句和存储程序的性能。

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