背景
我收到了一位客户的报告,该客户抱怨他们在服务器上执行了一些更新后,他们的日志文件中收到以下错误消息:
ERROR [HY000] [MySQL][ODBC 5.2(w) Driver][mysqld-5.1.49]Can't create more than max_prepared_stmt_count statements (current value: 16382)
我的发现
Google给了我以下interesting result。
阅读上面的博客文章后,我开始尝试,当然,当更新到新的ODBC驱动程序(5.2.2,又称“MySQL ODBC 5.2w驱动程序”)时,所有语句现在默认都是服务器端准备的。 (另见公告here)。
以下是使用5.2.2的全局日志中的两行:
[192.168.0.150]|134302|0|Prepare|INSERT INTO `a1gt6` (`TimeStamp`, `Temperature`) VALUES (?, ? )
[192.168.0.150]|134302|0|Execute|INSERT INTO `a1gt6` (`TimeStamp`, `Temperature`) VALUES ('2012-11-25 12:40:27', '7.03750000000000000e+001' )
问题
如果不是因为它们显然没有被服务器关闭(及时地,请参见下面的更新),我的陈述变成准备好的陈述,我不会有大问题,如下所示:上述错误消息和命令show global status like "com_stmt%";
Com_stmt_close 0
Com_stmt_execute 1
Com_stmt_fetch 0
Com_stmt_prepare 1
Com_stmt_reprepare 0
Com_stmt_reset 0
Com_stmt_send_long_data 0
上表显示了使用Connector / ODBC 5.2.2一次运行语句后的结果。以下是show global status like "com_stmt%";
使用Connector / ODBC / 5.1.11
Com_stmt_close 0
Com_stmt_execute 0
Com_stmt_fetch 0
Com_stmt_prepare 0
Com_stmt_reprepare 0
Com_stmt_reset 0
Com_stmt_send_long_data 0
以下是我用来确定问题原因的C#测试方法。
[TestMethod]
public void TestOldfashion()
{
int option = 16 + /*FLAG_MULTI_STATEMENTS*/ 67108864;
string odbcString = @"DRIVER={{{0}}};SERVER={1};UID={2};PWD={3};OPTION=" + option + ";CharSet=utf8;";
using( System.Data.Odbc.OdbcConnection con = new System.Data.Odbc.OdbcConnection( string.Format( odbcString, new object[] { "MySQL ODBC 5.1 Driver", "server", "user", "password" } ) ) ) {
con.Open();
con.ChangeDatabase( "fooDB" );
using( System.Data.Odbc.OdbcCommand cmd = con.CreateCommand() ) {
cmd.CommandText = "INSERT INTO myTable ( foo, value ) VALUES ( ?, ? );";
cmd.Parameters.AddWithValue( "foo", 2 );
cmd.Parameters.AddWithValue( "value", "asf" );
int i = cmd.ExecuteNonQuery();
}
}
}
这是表格的CREATE语句:
delimiter $$
CREATE TABLE `mytable` (
`index` int(11) NOT NULL AUTO_INCREMENT,
`foo` float DEFAULT NULL,
`value` text,
PRIMARY KEY (`index`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8$$
发布前更新
在提出这个问题之前,我再次执行了show global status like "com_stmt%";
,现在它给出了以下结果,所以看起来服务器 清理语句,但为时已晚(考虑到上面的错误信息)。即使在30分钟后,最后一个仍然未被封闭(Com_stmt_reset
)。
Com_stmt_close 6
Com_stmt_execute 7
Com_stmt_fetch 0
Com_stmt_prepare 7
Com_stmt_reprepare 0
Com_stmt_reset 1
Com_stmt_send_long_data 0
announcement提到可以通过向连接字符串添加no_ssps=1
选项来禁用服务器端准备。这似乎工作(只是简单地测试了它),但它不是一个坚实的解决方案。我宁愿尽可能多地使用默认值,因为我希望MySQL开发人员知道最佳实践是什么。可能我做错了什么或者没有正确理解事情。
问题
如果服务器现在将我的常规语句转换为预准备语句(而不是旧的客户端模拟),为什么不及时清理它以避免上述错误?
我希望在达到OdbcCommand的使用范围结束时关闭/释放语句,但这显然是不正确的?
如果有人能够对此有所了解,我会非常感激。不幸的是它是00:55所以我现在不能熬夜回答/澄清事情,但明天早上会再次办理登机手续。