如何解决MySQL错误“Prepared statement needs to be re-prepared”

29
我已经重写了我的网站PHP代码并添加了MySQL存储过程。 在我的本地版本中一切正常,但是在将网站上传到托管服务器后,我不断地收到致命错误“Prepared statement needs to be re-prepared”。 有时页面可以加载,有时加载失败并出现此错误。这是什么原因?

1
以下答案没有涉及到问题的核心。更多答案请参考此重复问题 - reinierpost
1
你的评论已经不再正确了。多年来出现了更多的答案,现在下面的答案似乎为重复问题增添了很多内容(而这些重复问题实际上并没有什么帮助)。 - Kar.ma
13个回答

40

4
如何提高table_definition_cache的值? - warmwhisky
3
当我在/etc/my.cnf文件(在[mysqld]部分下)中添加了table_definition_cache=5000并重新启动mysqld时,错误消失了。请注意,你的my.cnf文件可能存放在不同的位置。 - Sygmoral
3
MariaDB 10.0、10.1、10.2、10.3、10.4和10.1.34中似乎存在一个等价的bug:https://jira.mariadb.org/browse/MDEV-17124,需要类似的解决方法(增加`table_definition_cache`的值大于或等于`table_open_cache`)。 - Jordan Rieger

19

@docwhat的回答看起来很好,但在共享托管服务器上,并不是每个人都被允许更改table_open_cachetable_definition_cache选项。

由于这个错误与预处理语句有关,我尝试通过提供以下选项来“模拟”PDO中的预处理语句:

$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, [
    PDO::ATTR_EMULATE_PREPARES => true
]);

注意:实际上这是在一个Laravel 5.6项目中,我在config/database.php中添加了这个选项:
'connections' => [
    'mysql' => [
        'driver' => 'mysql',
        'host' => env('DB_HOST', '127.0.0.1'),
        'port' => env('DB_PORT', '3306'),
        'database' => env('DB_DATABASE', 'forge'),
        'username' => env('DB_USERNAME', 'forge'),
        'password' => env('DB_PASSWORD', ''),
        'unix_socket' => env('DB_SOCKET', ''),
        'charset' => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
        'prefix' => '',
        'strict' => true,
        'engine' => null,
        'options' => [
            PDO::ATTR_EMULATE_PREPARES => true,
        ],
    ],
    (...)
],

我还没有测试模拟预处理语句对于网站加载时间的影响,但它可以避免我得到错误提示 SQLSTATE[HY000]: General error: 1615 Prepared statement needs to be re-prepared
在性能方面有更新:模拟版本似乎要稍微快一点(32.7±1.4毫秒模拟,35.0±2.3毫秒正常,n=10,双尾学生T检验的p值为0.027)。

我也遇到了这个问题,这个修复方法很好用。但出于安全考虑,不建议使用(引用David评论)。但如果你愿意冒险或者别无选择,那就这样吧 :D - josevoid
1
到目前为止,我对准备的本地与PDO相关的安全问题的搜索没有发现任何差异。这里也是一个不错的起点。您认为模拟存在风险的原因是什么? - Marten Koetsier
我承认我对此有限的了解,只是依赖于我刚刚发现的一些简短信息。我曾经相信增加table_definition_cache是最合适的解决方案。但是当我阅读你提供的链接时,我开始怀疑了。实际上,我仍然面临这个问题,并且我观察到这在服务器php 7.0上发生,但不会在php 5.6上发生(这是我设置的特定情况)。 - josevoid
请注意,这会导致整数类型被获取为字符串。table_definition_cache 更可靠。 - Parsa_Gholipour

17
简而言之:不要在预处理语句中使用视图。
这似乎是一个持续存在的问题。
动态SQL处理视图很混乱。
最早的错误是11年前的无法在准备好的语句中创建视图。有一个补丁来解决它。 另一个错误报告当MySQL服务器负载过高时,准备的语句失败,指出当基础表繁忙时,错误1615不是错误。(真的吗?)
虽然增加表缓存大小有一定的价值(参见在使用mysql视图时的Mysql错误),但并不总是有效(参见常规错误:1615准备语句需要重新准备(选择mysql视图))。
替代方案 一年多以前,有人在MySQL论坛(MySql“视图”,“准备语句”和“准备语句需要重新准备”)中提到了这个问题。
有人提出不在准备好的语句中使用视图,而是在子查询中使用视图的SQL的简单想法。另一个想法是创建视图使用的SQL,并在客户端代码中执行它
这些似乎是比仅仅增加表缓存大小更好的解决办法。

4
实际上,我认为这是正确的答案,因为它指出了错误的原因,而不仅仅是通过更改服务器配置来处理影响!非常感谢 - 我会根据您的建议和链接资源修改我的代码,希望这将最终解决这个奇怪的错误! - SaschaP
1
这是一个非常糟糕的答案。在MySQL/MariaDB数据库中,使用预处理语句与视图确实可以工作(我有从TB级别的数据中对数百万个数据库表进行操作的经验,并且其中大部分都有视图)。更好的解决方案是确定适当的值用于table_xxx_cache(允许在您的环境中),或将SQL并发限制在范围内。 - J Jorgenson
@JJorgenson,您没有说明“适当的值”是什么意思。如何找到它,为什么它不起作用。 - TuralAsgar
需要一篇专门的文章来回答这个问题,但简短的答案是,“适当的值”可能是在任何给定时间使用的最大表数。我的服务器有超过5百万个表,但我们在任何给定时刻只有100,000个表,这就是我们缓存限制的设置位置。显然,更大的缓存需要更多的服务器内存/资源,这可能会对缓存大小施加限制。 - J Jorgenson

8

首先获取 mysql shell 访问权限:

mysql 

检查table_definition_cache的值:

show global variables like '%table_definition_cache%';

它可能是400或1400。

将其放大:

set global table_definition_cache = 4000;

准备就绪!


5

只需按照以下步骤操作:

SET GLOBAL table_definition_cache = 4096;
SET GLOBAL table_open_cache = 4096;

4096 可能会太小,因此请将其设置为更高的值。但是请确保两个值相同。


5
问题:'Prepared statement needs to be re-prepared'
此问题通常在使用任何计算机语言(如Java)或从后端调用过程时发生。
解决方案:通过执行以下脚本增加缓存大小。
脚本: set global table_definition_cache = 4000;

4
你要在何处运行上述脚本? - warmwhisky

3

在使用Doctrine ORM时,对数据库运行FLUSH TABLES命令可以解决我的问题。


这对我来说起作用了,而且没有修改任何项目代码。将table_definition_cache增加到1024在开发中有效,但在生产中无效。我认为在生产环境中1024不够用,因为我们在该系统上有更多的数据库和表。 - Nick B
这个回答需要更多的赞,说实话。我已经将它安排为一个定期运行的任务。自那以后就没有出现过问题了。 - waltmagic

1
我的解决方案是创建一个像这样的例程:

DELIMITER $$
--
-- Procedimientos
--
DROP PROCEDURE IF EXISTS `dch_content_class_content`$$

CREATE DEFINER=`renuecod`@`localhost` PROCEDURE `dch_content_class_content`(IN $classId INTEGER)
BEGIN
-- vw_content_class_contents is a VIEW (UNIONS)
  select * from vw_content_class_contents;
END$$

我希望这可以帮助某个人。

你为什么需要那种粗体和大字体? - Alexey Subach
1
抱歉,字体样式不必要。我希望我的贡献对某些人有用。 - Gabriel

0

好的,我们被困在一台笔记本电脑上,它无法让Tableau更新或从我们的MariaDB 10.3.31数据库视图中检索数据。我们做了通常的事情,谷歌和堆栈溢出,有很多解决方案,但都不起作用。然而,我们有另一台可以连接并正常运行的笔记本电脑。所以在多次挠头之后,一个灵光乍现的时刻来了。 有问题的笔记本电脑安装了V5.3.14和V8.0.26 ODBC连接器。我们删除了V8.0.26 ODBC连接器,所有视图问题都消失了。 希望有人会发现这个解决方案有用。


0

我遇到了一个由于 group_concat_max_len 语句过长而导致的错误。 SET SESSION group_concat_max_len = 1000000000000000000; 将其删除后,错误消失了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接