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

时间:2012-11-30 15:51:02

标签: c# mysql

我有.sql文件(550 MB),我想将其导入到运行的mysql服务器。我知道mysql.exe的路径。

我的想法是模仿命令行导入mysql -u user -ppass db_name < file.sql。这从命令行运行良好(我已设置高max_allowed_pa​​cket)。根据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();

但是我得到了OutOfMemory异常。所以正确的方法并不能通过字符串。

我非常感谢任何建议如何找出问题所在。我甚至不知道它的进程超时或mysql超时或者问题是否在StreamReader中。

4 个答案:

答案 0 :(得分:14)

我知道这不是您问题的直接答案,说实话,我不确定您的方法有什么问题。我可以通过分享我们如何使用mysql.exe运行非常大的sql脚本来帮助...

"C:\Program Files (x86)\MySQL\MySQL Server 5.0\bin\mysql.exe" -C -B --password=[password] -P 3306 --user=[username] --host=localhost --database=[database] -e "\. C:\Backups\Mybackup.sql"

这些参数中的大部分都是显而易见的,连接信息等。

显而易见的是,-e "\. [filename]" -e参数的神奇部分指明mysql应运行以下命令并退出。前缀"\. "表示应使用输入文件,后跟该文件名。

我们使用它来恢复数千兆字节的数据库而不会出现问题。所以这里是完整的&#39;运行scirpt&#39;用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();
    process.BeginErrorReadLine();
    process.BeginOutputReadLine();
    process.StandardInput.Close();
    process.WaitForExit();

    return process.ExitCode;
}

答案 1 :(得分:2)

我遇到了同样的问题并且使用已接受的解决方案太顽固了,尽管它已经两年没有受到挑战。此外,我想从.gz导入而不先将其解压缩到另一个文件(并且不想假设gzip可执行文件可用)。所以我找出了原始代码的错误。

MySQL转储文件可以有很长的行没有中断,所以我的猜测是ReadLine()填充缓冲区而没有找到换行符,导致OutOfMemory异常。因此,必须使用Read()代替ReadLine()来避免这种情况。

可能发生的另一个问题(我有)是在.sql文件中使用二进制blob,当将文本字符串从文件移动到stdin时,文本重新编码可能会搞乱,所以我&#39;修改了代码以避免使用读者或写入者,而是使用字节和二进制文件。

这是我的解决方案:

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未压缩)没有任何问题,但我欢迎任何代码改进。

答案 2 :(得分:0)

我遇到了很多问题,问题不是代码,代码是正确的,问题是虚拟内存的限制,计算机不支持使用每个进程内存大的文件。 Windows进程系统限制执行每个进程的内存量,以防止系统崩溃。

我不知道这个限制,因为它因每个计算机架构以及内存,总线等的数量而异。

您可以从文件中获得建议,按顺序创建表,读取一段运行,读取另一个运行,等等。

另一个想法是尝试在另一个线程或进程上并行工作,请注意:http://www.codeproject.com/Articles/189374/The-Basics-of-Task-Parallelism-via-C

答案 3 :(得分:0)

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