在C#中使用MySQL.Data Client如何释放Prepared Statement?
问题描述
使用MySQL.Data客户端库访问MySQL时,遇到以下异常:
Can't create more than max_prepared_stmt_count statements (current value: 16382)
代码中每页处理后都会关闭并释放数据库连接,但第4页仍触发错误,单独处理第4页可正常执行。根据官方文档,会话终止时服务器应自动释放预编译语句,但实际情况不符。
简化代码示例:
// 分页循环获取数据 while(ContinueSalesOrderPage(apiClient, ref pageNum, days, out string response, window) == true) { KeyValueTag error = HandleSalesOrderPageData(response, pageNum, out int numOrders, window); } private KeyValueTag HandleSalesOrderPageData(string response, int pageNum, out int numOrders, WaitWindow window) { numOrders = json.ArrayOf("List").Size; DatabaseWriter dbEngine = new DatabaseWriter() { Host = dbHost, Name = dbName, User = dbUser, Password = dbPass, }; bool pass = dbEngine.Connect(); for(int orderLoop = 0; orderLoop < numOrders; orderLoop++) { // 每次循环包含3-10+条预编译增改语句 KeyValueTag error = InsertOrUpdateSalesOrder(dbEngine, item, config, pageNum, orderLoop, numOrders, window); } dbEngine.Disconnect(); } // DatabaseWriter的Disconnect方法 public void Disconnect() { _CMD.Dispose(); _engine.Close(); _engine.Dispose(); }
问题根源
MySQL.Data默认启用连接池,调用Close()或Dispose()时,连接不会真的断开服务器会话,而是被放回连接池复用。之前会话中创建的预编译语句会保留在服务器端,随着循环次数增加,预编译语句数量累积超过max_prepared_stmt_count限制。
解决方案
1. 显式释放单个预编译语句
确保每个使用预编译语句的MySqlCommand都被正确释放,推荐用using语句自动管理生命周期:
// 在InsertOrUpdateSalesOrder方法内部 using (var cmd = new MySqlCommand(sql, dbEngine.Connection)) { // 设置参数 cmd.Parameters.AddWithValue("@Param1", value1); // 执行语句 cmd.ExecuteNonQuery(); }
MySqlCommand.Dispose()会自动向服务器发送DEALLOCATE PREPARE指令,释放当前预编译语句。
2. 复用预编译语句
对于重复执行的SQL语句,不要每次循环都创建新的MySqlCommand,复用同一个Command对象可以大幅减少预编译语句的创建数量:
// 在HandleSalesOrderPageData中提前创建复用的Command using (var insertCmd = new MySqlCommand(insertSql, dbEngine.Connection)) { // 提前定义参数 insertCmd.Parameters.Add("@OrderId", MySqlDbType.Int32); insertCmd.Parameters.Add("@Amount", MySqlDbType.Decimal); for(int orderLoop = 0; orderLoop < numOrders; orderLoop++) { // 仅更新参数值 insertCmd.Parameters["@OrderId"].Value = item.OrderId; insertCmd.Parameters["@Amount"].Value = item.Amount; insertCmd.ExecuteNonQuery(); } }
3. 禁用连接池(临时方案)
在连接字符串中添加Pooling=false,强制每次关闭连接时断开服务器会话,服务器会自动释放该会话的所有预编译语句:
Server=your_host;Database=your_db;Uid=user;Pwd=pass;Pooling=false;
注意:禁用连接池会降低性能,适合测试验证问题根源,不建议长期在生产环境使用。
4. 调整服务器参数(治标方案)
临时增大max_prepared_stmt_count的值,缓解当前问题,但无法从根本上解决预编译语句泄漏:
SET GLOBAL max_prepared_stmt_count = 32768;
若需永久生效,修改MySQL配置文件(my.cnf/my.ini):
max_prepared_stmt_count = 32768
内容的提问来源于stack exchange,提问作者SalmaBegum
相关产品推荐
相关产品推荐

