是,mysql在prepare或首次执行时校验表结构,后续复用缓存;alter table等变更会触发重检,优化阶段即锁定字段属性,select/insert错误均发生在执行前。

MySQL 执行 SQL 前是否检查表结构?
会,但不是每次执行都重新解析表结构。MySQL 在语句预处理(PREPARE)或首次执行时完成元数据校验,后续执行复用已缓存的执行计划(如启用 query_cache 或 prepared_statement 缓存),跳过结构检查。但以下情况会强制重新检查:
- 表被
ALTER TABLE修改后,相关缓存(如table_definition_cache中的 frm 信息、table_open_cache中的打开句柄)可能失效,下次执行时触发重载 - 使用了动态列引用(如
SELECT * FROM t WHERE col = ?)且列名在运行时才确定(极少见,多见于存储过程拼接 SQL) - 启用了
sql_mode=STRICT_TRANS_TABLES且语句含隐式类型转换,校验会延后到执行阶段
表结构访问发生在哪几个环节?
MySQL 访问表结构主要分三步:解析器查 information_schema.COLUMNS(仅 DDL 或 SHOW 语句)、优化器读取 TABLE_SHARE 内存结构、执行器调用 handler::open() 获取字段定义。关键点:
-
SELECT类查询在优化阶段就锁定字段数量、类型、NULL 属性,一旦SELECT a,b FROM t中b被删掉,语句直接报错Unknown column 'b' in 'field list',不进执行器 -
INSERT INTO t VALUES (...)依赖表定义推导字段顺序,若表结构变更导致字段数不匹配,错误在执行前就抛出Column count doesn't match value count - 视图或派生表的结构检查延迟到实际展开时,所以修改基表后首次查视图可能失败
为什么有时候改了表结构,SQL 还能跑?
常见于缓存未刷新或访问路径绕过元数据强校验。典型场景:
- 使用
mysql_real_query()C API 直接发包,服务端若已缓存TABLE_SHARE且未触发 invalidation(如没做FLUSH TABLES),可能读到旧结构 -
SELECT * FROM t在 prepared statement 模式下,如果 prepare 后表被加列,execute 仍按原字段数分配内存,新列值被忽略,不会报错但结果不完整 - 分区表中只修改某个分区的定义(非法操作,但某些旧版本允许),主表结构未变,查询可能成功返回部分数据
如何确认某次执行是否用了最新表结构?
最可靠方式是强制刷新元数据缓存并观察错误:
- 执行
FLUSH TABLES t清空该表缓存,再运行 SQL;若报错说明结构已变更且被检测到 - 查
performance_schema.table_handles(需开启performance_schema),看OBJECT_SCHEMA/OBJECT_NAME对应的INTERNAL_LOCK和EXTERNAL_LOCK状态是否活跃,活跃说明正在用当前结构 - 开启
general_log,搜索日志中是否有Opening tables+After opening tables,后者出现即表示已完成结构加载
注意:INFORMATION_SCHEMA 表本身是只读视图,其内容来自内存缓存,不一定反映磁盘 .frm 或数据字典的实时状态。










