结论是MySQL服务端缓存机制触发强制重准备,主因是table_open_cache或table_definition_cache设置过小;需检查并协同调大两者,尤其在大量视图、存储过程或高并发表访问场景下。

直接结论:这不是你代码写错了,而是 MySQL 服务端缓存机制触发的强制重准备,最常见原因是 table_definition_cache 或 table_open_cache 设置过小,尤其在频繁访问视图、存储过程或大量表的场景下极易复现。
查当前缓存值是否过低
这个错误往往不是突然出现的,而是随着业务表数量增长、视图嵌套变深、或批量执行存储过程后才爆发。先确认是不是缓存瓶颈:
- 运行
SHOW VARIABLES LIKE '%table_open_cache%';,检查table_open_cache值 —— 默认值常为 2000 或更低,不够用 - 运行
SHOW VARIABLES LIKE '%table_definition_cache%';,检查table_definition_cache值 —— 默认可能仅 400~2000,而实际需要的表定义缓存远高于此 - 如果应用中用了大量视图(尤其是跨库、含子查询或 JOIN 的视图),
table_definition_cache尤其关键;每个视图、基表、临时表结构都会占用一个定义缓存槽位
调大两个关键缓存参数
不要只调一个,这两个参数协同工作,单独调大一个效果有限:
-
table_open_cache控制同时打开的表文件句柄数,影响并发打开能力;建议设为max_connections × 平均每连接访问表数,常见安全值是16384 -
table_definition_cache控制已加载的表/视图/存储过程元数据缓存条目数;官方建议公式为(table_open_cache / 2) + 400,但高复杂度视图场景建议直接设为16384 - 执行命令(需 SUPER 权限):
SET GLOBAL table_open_cache = 16384;SET GLOBAL table_definition_cache = 16384; - 注意:该设置仅对新连接生效;已有连接仍可能报错,需重启应用连接池或等待旧连接自然释放
为什么 PDO/Java PreparedStatement 还会触发?
即使你代码里用了 prepare → execute 流程,MySQL 服务端仍可能在中间“丢掉”预编译状态,原因包括:
- 服务端缓存被挤出:当
table_definition_cache满时,MySQL 会淘汰旧表定义,导致依赖它的预编译语句失效,下次EXECUTE就报ERROR 1615 - 存储过程或视图结构变更后未刷新缓存:比如 ALTER VIEW、ADD COLUMN 后,旧预编译语句若引用了该对象,就无法复用原有计划
- MySQL 8.0.22 存在已知 bug:当
group_concat_max_len> 4294967295 时会误触发该错误,已在 8.0.26 修复 —— 若你用的是这个小版本,升级是最稳妥解法 - PDO 中反复
prepare同一 SQL 字符串(如循环内 new PDO → prepare)看似“安全”,实则绕过了客户端缓存,也加重服务端压力,不推荐
容易忽略的线上细节
这个问题在线上常被误判为“代码问题”,但真正根因藏在服务端配置和使用模式里:
- RDS 或云数据库用户不能直接执行
SET GLOBAL?那就去控制台修改参数组,设置后必须重启实例(部分云厂商支持热生效,但需确认文档) - 调参不是越大越好:
table_open_cache过大会显著增加文件描述符消耗,可能触发系统级限制(ulimit -n) - 如果你的应用大量使用临时表(
CREATE TEMPORARY TABLE),它们也会计入table_definition_cache,这点常被忽视 - 错误日志里若伴随
Opened_tables持续上升,基本可锁定是table_open_cache不足;用SHOW STATUS LIKE 'Opened_tables';对比前后值


















