【问题标题】:How to import large SQL file using mysql.exe through StreamReader -> StandardInput如何通过 StreamReader -> StandardInput 使用 mysql.exe 导入大型 SQL 文件
【发布时间】:2012-11-30 15:51:01
【问题描述】:

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

【问题讨论】:

  • 为了您的信息,我通过在 Temp 目录中创建运行“mysql

标签: c# mysql


【解决方案1】:

我知道这不是您问题的直接答案,老实说,我不确定您的方法有什么问题。我可以通过分享我们如何使用 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应该运行以下命令并退出的神奇部分。前缀"\. " 表示应使用输入文件,后跟该文件名。

我们使用它来毫无问题地恢复数 GB 的数据库。所以这里是完整的用 mssql 'run a scirpt'...

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;
}

【讨论】:

  • 可以使用带有上述 \"{5}\"" 示例后缀的 SQL 内联,其中 5 = SQL 而不是文件名
  • 在你的代码示例中,进程是不是启动了两次(第一次使用var process = Process.Start(),第二次调用process.Start())?
  • 谢谢,谢谢,谢谢!.. 我正用头撞桌子,想知道为什么< 在我的.Net 进程中没有工作,而是在cmd 线上。 -e 是诀窍。
【解决方案2】:

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

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

可能发生的另一个问题(我遇到过)是 .sql 文件中的二进制 blob,当将文本字符串从文件移动到标准输入时,文本重新编码可能会搞砸,所以我已经修改避免使用 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),没有任何问题,但我欢迎任何代码改进。

【讨论】:

  • +1 使用此选项,过程和方法生命周期保持同步,如果您想在之后对 .sql 文件执行其他操作,这非常有用。接受的答案不适用于我的用例。
【解决方案3】:

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

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

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

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 2015-11-25
      • 2014-07-10
      • 2010-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-19
      • 1970-01-01
      相关资源
      最近更新 更多