5

我有 .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

4 回答 4

16

我知道这不是您问题的直接答案,老实说,我不确定您的方法有什么问题。我可以通过分享我们如何使用 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"

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

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

我们使用它来毫无问题地恢复数 GB 的数据库。所以这是用 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;
}
于 2012-12-04T18:34:11.207 回答
2

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

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

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

这是我的解决方案:

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

于 2015-01-23T11:33:23.313 回答
0

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

我不知道限制,因为它因每种计算机体系结构和内存、总线等的数量而异。..

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

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

于 2012-12-04T11:09:23.203 回答
0

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

于 2012-12-07T15:28:44.803 回答