You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 22:20:40