对于客户端可能在会话期间多次执行的某些语句,服务器会将语句转换为内部结构并缓存该结构以在执行期间使用。缓存使服务器能够更有效地执行,因为它避免了在会话期间再次需要时重新转换语句的开销。这些语句发生转换和缓存:
准备好的语句,包括在 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 *
对访问表或视图的程序内的 a进行重新分析,但不对SELECT *
不访问表或视图的 a 进行重新分析。当一条语句受到影响时,如果可能,服务器只会对其进行部分重新解析。考虑这个
CASE
声明:CASE case_expr WHEN when_expr1 ... WHEN when_expr2 ... WHEN when_expr3 ... ... END CASE
如果元数据更改仅影响,则重新解析该表达式。 并且其他表达式不会被重新解析。
WHEN
when_expr3
case_expr
WHEN
重新分析使用对原始转换为内部形式有效的默认数据库和 SQL 模式。
服务器最多尝试重新解析 3 次。如果所有尝试都失败,则会发生错误。
重新解析是自动进行的,但就其发生而言,会降低准备语句和存储程序的性能。
对于准备好的语句,
Com_stmt_reprepare
状态变量跟踪重新准备的次数。