背景
我收到了一位客户的报告,他抱怨说,在他们的服务器上执行了一些更新后,他们的日志文件中出现了以下错误消息:
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)
我的发现
谷歌给了我以下有趣的结果。
在阅读了上面的博客文章后,我开始进行实验,果然 - 当更新到新的 ODBC 驱动程序(5.2.2,又名“MySQL ODBC 5.2w 驱动程序”)时,所有语句现在默认情况下都是服务器端准备的。(另请参阅此处的公告)。
这是使用 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%";
再次执行了,现在它给出了以下结果,所以看起来服务器确实清理了语句,但为时已晚(考虑到上述错误消息)。Com_stmt_reset
即使在 30 分钟后,最后一个仍然未关闭(in )。
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
该公告提到可以通过将no_ssps=1
选项添加到连接字符串来禁用服务器端准备。这似乎有效(仅对其进行了简要测试),但感觉不像是一个可靠的解决方案。我宁愿尽可能地使用默认值,因为我希望 MySQL 开发人员知道最佳实践是什么。可能我做错了什么或没有正确理解事情。
问题
如果服务器现在将我的常规语句转换为准备好的语句(与旧的客户端仿真相反),为什么不及时清理它以避免上述错误?
我希望在 OdbcCommand 的使用范围结束时关闭/释放该语句,但这显然不正确?
如果有人能对此有所了解,我将不胜感激。不幸的是,现在是 00:55,所以我现在不能熬夜回答/澄清事情,但明天早上会再次检查。