如何通过StreamReader ->StandardInput使用mysql.exe导入大型SQL文件

8wigbo56  于 2022-12-22  发布在  Mysql
关注(0)|答案(4)|浏览(111)

我有.sql文件(550 MB),我想将其导入到运行mysql服务器。我知道路径mysql.exe。
我的想法是模仿命令行导入mysql -u user -ppass db_name < file.sql。这从命令行工作得很好(我已经设置了高max_allowed_packet)。根据另一个线程在这里Stackoverflow我发现这工作:

Process process = new Process();
process.StartInfo.FileName = mysqlexepath;
process.StartInfo.Arguments = "-v -u user -ppassworddbname";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;

try
{
    process.Start();
    StreamWriter input = process.StandardInput;
    using (StreamReader sr = new StreamReader(sqlfilepath))
    {
        while ((line = sr.ReadLine()) != null)
        {
            if (process.HasExited == true)
                throw new Exception("DB went away.");

            input.WriteLine(line);
            input.Flush();
        }
    }
    process.Close();
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message);
}

我可以看到表是如何在DB中创建的。但我的问题是,在大约一半的进程退出。我在谷歌上搜索一些超时设置,但找不到任何东西。
我还尝试先读取文件:

var file = FileInfo(sqlfilepath);
StreamReader reader = file.OpenText();
string fileContents = reader.ReadToEnd();
StreamWriter input = process.StandardInput;
input.AutoFlush = true;
input.Write(fileContents);
input.Close();

但是我得到了内存不足的异常,所以正确的方法不是通过字符串。
我将非常感谢任何关于如何找出问题出在哪里的建议。我甚至不知道是它的进程超时还是MySQL超时,或者问题出在StreamReader中。

ycl3bljg

ycl3bljg1#

我知道这不是你的问题的直接答案,老实说,我不知道你的方法有什么问题。我可以通过分享我们如何使用mysql.exe运行非常大的sql脚本来帮助你...
第一个月
这些参数中的大多数是显而易见的,例如连接信息等。
不太明显的是-e "\. [filename]"的神奇部分-e参数指定mysql应该运行下面的命令并退出,前缀"\. "表示应该使用一个输入文件,后面跟一个文件名。
我们用它来恢复数GB的数据库没有问题。所以这里是完整的'运行scirpt'与mssql...

public static int RunMySql(string server, int port, string user, string password, string database, string filename)
{
    var process = Process.Start(
        new ProcessStartInfo
        {
            FileName = @"C:\Program Files (x86)\MySQL\MySQL Server 5.0\bin\mysql.exe",
            Arguments =
                String.Format(
                    "-C -B --host={0} -P {1} --user={2} --password={3} --database={4} -e \"\\. {5}\"",
                    server, port, user, password, database, filename),
            ErrorDialog = false,
            CreateNoWindow = true,
            UseShellExecute = false,
            RedirectStandardError = true,
            RedirectStandardInput = true,
            RedirectStandardOutput = true,
            WorkingDirectory = Environment.CurrentDirectory,
        }
        );

    process.OutputDataReceived += (o, e) => Console.Out.WriteLine(e.Data);
    process.ErrorDataReceived += (o, e) => Console.Error.WriteLine(e.Data);
    // process.Start(); NOTE: NO need to start the process, because Process.Start has already started the process
    process.BeginErrorReadLine();
    process.BeginOutputReadLine();
    process.StandardInput.Close();
    process.WaitForExit();

    return process.ExitCode;
}
ikfrs5lh

ikfrs5lh2#

我也遇到过同样的问题,我太固执了,没有使用公认的解决方案,尽管它已经两年没有受到挑战了。而且,我想从一个. gz导入,而不需要先将它解压缩到另一个文件(也不想假设一个gzip可执行文件是可用的)。所以我找出了原始代码的问题所在。
MySQL转储文件可能有很长的行,没有换行符,所以我猜测ReadLine()在没有找到换行符的情况下填充了缓冲区,从而导致OutOfMemory异常,因此必须使用Read()而不是ReadLine()来避免这种情况。
另一个可能发生的问题(我也遇到过)是. sql文件中的二进制blob,当将文本字符串从文件移动到stdin时,文本重新编码可能会导致混乱,因此我修改了代码以避免使用Readers或Writers,而是使用bytes和binary。
我的解决方案是:

Process process = new Process();
process.StartInfo.FileName = mysqlexepath;
process.StartInfo.Arguments = "-v -u user -ppassworddbname";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;

byte[] buffer = new byte[4096]; 
int count;

try
{
    process.Start();
    BinaryWriter stdinBinary = new BinaryWriter(process.StandardInput.BaseStream);

    Stream fileStream;
    if (sqlfilepath.EndsWith(".gz")) {
         fileStream = new GZipStream(new FileStream(sqlfilepath, FileMode.Open, FileAccess.Read, FileShare.Read), CompressionMode.Decompress); 
    } else {
         fileStream = new FileStream(sqlfilepath, FileMode.Open, FileAccess.Read, FileShare.Read);
    }

    using (fileStream)
    {
        while ((count = fileStream.Read(buffer, 0, buffer.Length)) > 0)
        {
            if (process.HasExited == true)
                throw new Exception("DB went away.");

            stdinBinary.Write(buffer, 0, count);
            stdinBinary.Flush(); // probably not needed
        }
    }

    stdinBinary.Flush();
    process.Close();
}
catch (Exception ex)
{
    Console.Error.WriteLine(ex.Message);
}

我导入了一个1.1 GB的sql.gz数据库转储文件(未压缩的3.5 GB),没有任何问题,但是我欢迎任何代码改进。

np8igboo

np8igboo3#

我遇到了这么多的问题,问题不在于代码,你的代码是正确的,问题是虚拟内存的限制,计算机不支持每个进程的内存这么大的文件工作。Windows进程的系统限制执行每个进程的内存量,以防止系统崩溃。
我不知道这个限制,因为它随每个计算机体系结构和内存、总线等的数量而变化。
建议你从文件中,按顺序创建表,读一段运行,读另一段运行,等等.
另一个想法是尝试在另一个线程或进程上并行工作,请看下面的内容:http://www.codeproject.com/Articles/189374/The-Basics-of-Task-Parallelism-via-C

jw5wzhpr

jw5wzhpr4#

在调试.NET应用程序时,windbg+SOS是一个很好的工具。

相关问题