如何摆脱 MySQL 错误“需要重新准备准备好的语句”
Posted
技术标签:
【中文标题】如何摆脱 MySQL 错误“需要重新准备准备好的语句”【英文标题】:How to get rid of MySQL error 'Prepared statement needs to be re-prepared' 【发布时间】:2010-12-07 19:36:32 【问题描述】:我已经重写了我的网站 php 代码并添加了 mysql 存储过程。
在我的本地版本中,一切正常,但在我将网站上传到托管服务器后,我不断收到致命错误“需要重新准备准备好的声明”。
有时页面加载,有时加载失败,我看到此错误。那是什么?
【问题讨论】:
下面的答案没有触及问题的核心。更多答案在this duplicate。 您的评论不再正确。多年来出现了更多答案,现在下面的答案似乎为重复增加了很多(确实没有什么帮助) 【参考方案1】:这是一种可能:MySQL bug #42041
他们建议提高table_definition_cache
的价值。
你可以阅读statement caching in the MySQL docs。
【讨论】:
如何提高 table_definition_cache 的值? 当我在我的/etc/my.cnf
文件中添加table_definition_cache=5000
(在[mysqld]
部分下)并重新启动mysqld
时,错误消失了。请注意,my.cnf
可能对您而言位于不同的位置。
也似乎是 MariaDB 10.0、10.1、10.2、10.3、10.4、10.1.34 中的等效错误:jira.mariadb.org/browse/MDEV-17124 具有类似的解决方法(增加 table_definition_cache
以大于或等于 @ 987654332@).【参考方案2】:
@docwhat's 的答案看起来不错,但在共享托管服务器上,并非所有人都可以触摸table_open_cache
或table_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.4ms 仿真,35.0±2.3ms 正常,n=10,p 值=0.027 对于双尾学生 T 检验)。
【讨论】:
我也面临这个问题,这个修复工作正常。但出于安全原因,不建议这样做(citing comment of David)。但是,如果您愿意冒险或别无选择,那就这样吧:D 到目前为止,我对与准备好的本机与 PDO 相关的安全问题的搜索没有产生任何差异。 Here 也是一个不错的起点。您有什么理由认为仿真存在风险? 我承认我对此了解有限,仅依赖于我刚刚找到的一些简短信息。我相信增加table_definition_cache
是这里最合适的解决方案。但是当我阅读您的链接时,我开始怀疑。实际上我仍然面临这个问题,我观察到这发生在服务器 php 7.0 而不是 php 5.6 上(这是特定于我的设置)
请注意,它会导致整数类型被提取为字符串。 table_definition_cache
更可靠。【参考方案3】:
简而言之:不要在准备好的语句中使用 VIEWS。
这似乎是一个持续存在的问题
动态 SQL 处理视图很混乱
最早的错误是Cannot create VIEWs in prepared statements from 11 years ago. There was a patch put in to address it.
另一个错误报告Prepared-Statement fails when MySQL-Server under load 指出,当基础表繁忙时,错误 1615 不是错误。 (真的吗?)
虽然增加表缓存大小有一些好处(请参阅MySql error when working with a mysql view), it does not always work (See General error: 1615 Prepared statement needs to be re-prepared (selecting mysql view))
替代方案 一年多前,有人在MySQL Forum (MySql “view”, “prepared statement” and “Prepared statement needs to be re-prepared”) 中提到了这一点。
有人想出了the simple idea of not using the view in the prepared statement but using the SQL of view in a subquery instead。另一个想法是create the SQL used by the view and execute it in your client code。
这些似乎是更好的解决方法,只是增加了表缓存大小。
【讨论】:
实际上我认为这是正确的答案,因为它指出了错误的原因,而不仅仅是通过更改服务器配置来处理效果!非常感谢 - 我会根据您的建议和链接资源更改我的代码,希望这最终能解决这个奇怪的错误! 这是一个非常糟糕的答案。对 MySQL/MariaDB 数据库使用带有视图确实的准备好的语句。 (我从处理数百万个数据库表中的 TB 数据的经验中知道,其中大多数都有视图)。更好的解决方案是确定 table_xxx_cache 的适当值(允许您的环境)或将 SQL 并发限制在限制范围内。 @JJorgenson 您没有指定“适当值”的含义。如何找到它,为什么它不起作用。 (需要专门的文章),但对于简短的回答,“适当的值”可能是在任何给定时间点使用的最大表数。我的服务器有超过 5M 的表,但是在任何给定点我们只有 100,000 个表,这就是我们设置缓存限制的地方。显然,更大的缓存需要更多的服务器内存/资源,这可能会对缓存大小施加限制。【参考方案4】:问题:'准备好的语句需要重新准备'
此问题通常发生在使用任何计算机语言(如 Java)或从后端调用过程调用过程时。
解决方案:通过使用(执行)下面的脚本来增加缓存的大小。
脚本:设置全局 table_definition_cache = 4000;
【讨论】:
你在哪里运行上面的脚本?【参考方案5】:首先获得对mysql
shell 的访问权限:
mysql
检查table_definition_cache的值:
show global variables like '%table_definition_cache%';
可能是 400 或 1400。
放大:
set global table_definition_cache = 4000;
一切顺利!
【讨论】:
【参考方案6】:我的解决方案是创建这样的例程:
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$$
我希望这对某人有所帮助
【讨论】:
你为什么需要那种又粗又大的字体? 对不起,字体样式不是必需的。我希望我的贡献对某人有用【参考方案7】:冲洗表;数据库上的命令为我解决了,我使用的是教义 orm。
【讨论】:
这对我有用,无需修改任何项目代码。将 table_definition_cache 增加到 1024 在开发中有效,但在生产中无效。我假设 1024 在生产中根本不够用,因为我们在该系统上有更多的数据库和表。【参考方案8】:这样做:
SET GLOBAL table_definition_cache = 4096;
SET GLOBAL table_open_cache = 4096;
4096 可以小一点,所以设置为大一点。但是要确保两个值是一样的。
【讨论】:
【参考方案9】:对于那些使用共享主机并且不想冒险使用 sql 注入的人来说,这是一种解决方法。根据这篇文章: Laravel Fluent Query Builder Join with subquery 您可以将视图的定义存储在函数中
private function myView()
return DB::raw('(**definition of the view**) my_view_name)');
然后像这样使用它:
public function scopeMyTable(Builder $query)
return $query->join($this->myView(),'my_view_name.id','=','some_table.id');
这是一种 laravel 方法,但我确信它可以在大多数情况下应用,并且不需要大量的代码重构或架构更改。此外,它相对安全,因为您的报表保持准备就绪
【讨论】:
【参考方案10】:这个错误是由一个大的group_concat_max_len
语句引起的
SET SESSION group_concat_max_len = 1000000000000000000;
删除它,错误消失了
【讨论】:
【参考方案11】:好的,我们被困在笔记本电脑上,不允许 Tableau 从 MariaDB 10.3.31 数据库的视图中更新或检索数据。我们做了通常的谷歌和堆栈溢出,很多解决方案都行不通。但是,我们有另一台笔记本电脑可以连接并运行良好。因此,在多次挠头之后,灵光乍现的时刻到来了。 有问题的笔记本电脑同时安装了 V5.3.14 和 V8.0.26 ODBC 连接器。我们删除了 V8.0.26 ODBC 连接器和 Bang,所有视图问题都消失了。 希望有人觉得这个解决方案有用。
【讨论】:
【参考方案12】:我尝试运行一个正在连接视图和表的 UPDATE 查询,但也遇到了同样的错误。由于我没有用户输入,我决定运行 DB::unprepared
来解决问题。
您可以在Laravel Documentation 中了解更多信息。
【讨论】:
【参考方案13】:我在共享托管环境中的 Ruby on Rails 中遇到了同样的错误。它可能不是最安全的解决方案,但禁用准备好的语句对我来说消除了错误消息。
这可以通过将“prepared_statements: false”设置添加到您的 database.yml 文件来完成:
production:
prepared_statements: false
当您无法控制 MySQL 服务器上的配置设置时,这似乎是一个合理的解决方案。
【讨论】:
以上是关于如何摆脱 MySQL 错误“需要重新准备准备好的语句”的主要内容,如果未能解决你的问题,请参考以下文章