バックグラウンド
サーバーでいくつかの更新を実行した後、ログファイルに次のエラーメッセージが表示されたという顧客からの報告がありました。
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を使用したグローバルログからの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 /ODBC5.2.2を使用してステートメントを1回実行した後の結果を示しています。以下は、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のusing-scopeの終わりに達したときにステートメントが閉じられる/解放されることを期待しますが、それは明らかに間違っていますか?
誰かがこれに光を当てることができれば幸いです。残念ながら00:55なので、今は答えたり明確にしたりすることはできませんが、明日の朝にまたチェックインします。