[教程] C# 修改 max_allowed_packet 不生效,给你两个方案!

问题描述

MySQL 默认 Max_Allowed_Packet 约为 4MB, 当我导入一个 19MB 左右的 .sql 文件时报错,错误如下:

System.ArgumentException: Option not supported. (Parameter 'max_allowed_packet')
   at MySql.Data.MySqlClient.MySqlConnectionStringBuilder.GetOption(String key)
   at MySql.Data.MySqlClient.MySqlConnectionStringBuilder.set_Item(String keyword, Object value)
   at System.Data.Common.DbConnectionStringBuilder.set_ConnectionString(String value)

通过执行 SET GLOBAL max_allowed_packet=20000000; 后依旧是初始值,但是下次启动程序却发现修改又成功了。

问题解答

经过不懈努力,寻求并整理得到以下 3 个结论:

  1. MySQL 在同一个 mysql 实例(同一个 Connection)中 使用SET GLOBAL max_allowed_packet=20000000; 修改了 paket size 在当前的 Connection 中无效,只有在下个 mysql 连接中生效
  2. MySQLConnect 通过依次调用 Close 、 Open 实现的 Connect 重连无法实现修改生效
  3. 通过SET GLOBAL max_allowed_packet=20000000; 修改了 paket size 在下次重启 MySql Service 后失效。(这是我的预期,我只希望临时生效)

解决方案:

问题都找到且失效原因都明确后,我找到了 2 个解决方案,但不管怎么说就是一个核心思路:保证在连接数据库前开一个新的进程来修改 max_allowed_packet , 随着进程的消亡,mysql 实例和连接释放,下个 Connection(也就是我们的 Connection)必然会生效。

方案一

在使用 MySQLConnection 连接前先调用 mysql.exe 设置一下 max_allowed_packet 的值,具体逻辑如下:

// 调用逻辑
public void Foo()
{
  var sqlFile = your .sql  file path here;
  ModifyMaxAllowedPacket(sqlFile);
// 这里开始你的连接
   var connStr = $"server={your IP};port={your Port};user={your User};password={your psw};charset=utf8";
   using var conn = new MySqlConnection(connStr);
    conn.Open();
// 这里开始你的 .sql  文件的导入
    var sql = File.ReadAllText(sqlFile);
    using (var importCmd = new MySqlCommand(sql, conn))
    {
        importCmd.CommandTimeout = 0;
        var result = importCmd.ExecuteNonQuery();
        Console.WriteLine($"执行 {sqlFile} 完毕,数据库新增数据 {result} 笔!");
    }    
  }

// 使用 mysql.exe 修改 max allowed packet
    private void ModifyMaxAllowedPacket(string sqlFile,string password)
    {
        var fileinfo = new FileInfo(sqlFile);
        try
        {
            // 通过 mysql.ServiceName 服务属性获取 mysql 的安装路径
            // 默认服务名称是: MySQL56
            if (serviceHandler.IsExistsService("MySQL56")) 
            {
                var mysqlPath = GetServiceExecPath(mysql.ServiceName);
                var pattern = @"^""(.*\\bin)\\.*$";
                var match = Regex.Match(mysqlPath, pattern);
                if (match.Success)
                {
                    var mysql_file = Path.Combine(match.Groups[1].Value, "mysql.exe");
                    if (!File.Exists(mysql_file))
                    {
                        throw new FileNotFoundException("未找到 mysql.exe 文件!");
                    }
// 请根据实际情况编写,另密码与-p之间没有空格
                    var args = @$"-u root -p{password} -h 127.0.0.1 -P 3306";
                    var process = new Process
                    {
                        StartInfo = new ProcessStartInfo
                        {
                            FileName = mysql_file,
                            Arguments = args,
                            UseShellExecute = false,
                            RedirectStandardInput = true,
                            CreateNoWindow = true
                        }
                    };
                    process.Start();
// 设置 max_allowed_packet 
                    process.StandardInput.WriteLine($"SET GLOBAL max_allowed_packet={fileinfo.Length * 2};");
// 退出 mysql 
                    process.StandardInput.WriteLine("exit");
                    process.WaitForExit();
                }
                 Console.WriteLine($"执行 SizeModifier 完成!");
            }
            else
            {
                throw new Exception($"未发现 {mysql.ServiceName} 服务,无法修改 max_allowed_packet!");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
    public string GetServiceExecPath(string serviceName)
    {
        string key = $@"SYSTEM\CurrentControlSet\Services\{serviceName}";
        using (RegistryKey serviceKey = Registry.LocalMachine.OpenSubKey(key))
        {
            if (serviceKey == null)
            {
                throw new Exception($"Service {serviceName} does not exist.");
            }
            return serviceKey.GetValue("ImagePath").ToString();
        }
    }

方案二

写一个控制台程序 MySQLMaxPackageSizeModifier 实现对 max_allowed_packet 的修改,在使用 MySQLConnection 连接前先调用这个控制台程序设置一下 max_allowed_packet 的值,具体逻辑如下:

MySQLMaxPackageSizeModifier.exe 调用逻辑:

// Usage: MySQLMaxPackageSizeModifier <server> <port> <user> <password> <max_allowed_packet>
// 本程序会使用传入的 user + psw 连接并修改 max allowed packet 的值为传入值(如果大于当前值)
// 程序执行完毕后,会自动关闭。

 var maxPacket = 1024 * 1024 * 1024; // 1G
 var maxPacketModifier = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MySQLMaxPackageSizeModifier.exe");
 var args = $"{mysql.IP} {mysql.Port} {mysql.User} {password} {maxPacket}";
 var process = new Process
 {
     StartInfo = new ProcessStartInfo
     {
         FileName = maxPacketModifier,
         Arguments = args,
         UseShellExecute = false,
         RedirectStandardOutput = true,
         CreateNoWindow = true
     }
 };
 process.Start();
 process.WaitForExit();
 var message = process.StandardOutput.ReadToEnd().Trim();
 Console.WriteLine($"执行 SizeModifier 完成!返回了:{message}");

// 接下来正式开始你的 MySQLConnection 的连接和数据的写入逻辑

MySQLMaxPackageSizeModifier 的实现:

using MySql.Data.MySqlClient;

class Program
{
    static void Main(string[] args)
    {
        if (args.Length < 5)
        {
            Console.WriteLine("Usage: MySQLMaxPackageSizeModifier <server> <port> <user> <password> <max_allowed_packet>");
            return;
        }

        string server = args[0];
        string port = args[1];
        string user = args[2];
        string password = args[3];
        int maxAllowedPacket = int.Parse(args[4]);

        string connStr = $"server={server};port={port};user={user};password={password};charset=utf8";

        try
        {
            using (var conn = new MySqlConnection(connStr))
            {
                conn.Open();
                // 先查询当前 max_allowed_packet 的值
                var currentMaxAllowedPacket = 0;
                var sql = "SHOW VARIABLES LIKE 'max_allowed_packet';";
                using (var cmd = new MySqlCommand(sql, conn))
                {
                    using var reader = cmd.ExecuteReader();
                    if (reader.Read()) // 读取结果集中的第一行
                    {
                        // Value 列是第二列,索引为 1
                        var maxValue = reader.GetString(1);
                        currentMaxAllowedPacket = int.Parse(maxValue);
                    }
                }
                if (currentMaxAllowedPacket < maxAllowedPacket)
                {
                    string setMaxAllowedPacketQuery = $"SET GLOBAL max_allowed_packet={maxAllowedPacket};";
                    using (var cmd = new MySqlCommand(setMaxAllowedPacketQuery, conn))
                    {
                        cmd.ExecuteNonQuery();
                    }
                    Console.WriteLine($"Successfully set max_allowed_packet to {maxAllowedPacket}.");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

写到最后

我最先实现的是本文第二种解决方案,但是由于需要自己再多维护一个程序,属实不优雅;因此使用了本文第一种方法,通过 MySQL 服务定位、运行并设置 Packet 后全身而退,Prefect!

扩展阅读

  1. 为什么修改 packet size 后不能立刻生效?
  2. 怎么通过 MySQL 服务定位 mysql.exe 安装路径?
  3. 如何通过修改 my.ini 配置实现对 packet size 的永久修改
  4. MySQL 官方 packet-too-large 相关

Tips:使用 SHOW VARIABLES LIKE 'max_allowed_packet'; 可以查询当前 packet size 的设置值。

笔者原创,版权所有,转载请注明出处!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352