【问题标题】:Memory leak when inserting into MySQL with Powershell v4使用 Powershell v4 插入 MySQL 时发生内存泄漏
【发布时间】:2014-05-04 05:29:50
【问题描述】:

我在 W2K12 R2(完全修补)上使用 powershell v4 将大量(100+ 百万)条记录插入 MySQL 数据库。我遇到了一个问题,尽管积极地删除变量和垃圾收集,内存使用量仍在不断增长。请注意,我正在运行脚本的机器上的内存使用量正在增长 - 而不是数据库服务器。

插入速度不错,作业运行良好。但是,我有一个内存泄漏,并且已经将我的头撞到墙上一个星期试图找出原因。我从测试中知道,内存在调用脚本的 MySQL 部分时会累积,而不是在其他任何地方。

我注意到,每次插入后,内存都会从 1MB 到 15MB 之间的任何地方增长。

这是该过程的基本流程(代码在底部)。 - 将记录添加到数组中,直到数组中有 1,000 条记录 -一旦有一千条记录,它们就会作为一个批次插入到数据库中 - 然后使用 .clear() 方法清空数组(我已经验证数组中保留了 0 条记录)。 -我在每次插入后都尝试过积极地收集垃圾(那里没有运气)。 -还尝试删除变量然后进行垃圾收集。还是没有运气。

为简洁起见,下面的代码进行了简化。但是,它显示了我如何遍历记录并进行插入:

$reader = [IO.File]::OpenText($filetoread)
$lineCount = 1
   while ($reader.Peek() -ge 0) {
      if($lineCount -ge 1000-or $reader.Peek() -lt 0) {

          insert_into_db

          $lineCount = 0
      }
   $lineCount++
   }
$reader.Close()
$reader.Dispose()

一次调用建立连接:

[void][system.reflection.Assembly]::LoadFrom("C:\Program Files (x86)\MySQL\MySQL Connector Net 6.8.3\Assemblies\v4.5\MySql.Data.dll")
$connection = New-Object MySql.Data.MySqlClient.MySqlConnection($connectionString)

这里是对 MySQL 的调用,为每 1,000 条记录执行实际插入:

function insert_into_db {
    $command = $connection.CreateCommand()                  # Create command object
    $command.CommandText = $query                           # Load query into object
    $script:RowsInserted = $command.ExecuteNonQuery()       # Execute command
    $command.Dispose()                                      # Dispose of command object
    $command = $null
    $query = $null
}

如果有人有任何想法或建议,我会全力以赴!

谢谢, 杰里米

【问题讨论】:

  • 您是否尝试过每 1000 行创建和处理一次连接对象以查看它是否会影响内存泄漏?不是最有效地利用资源,但可以提供信息(如果有用,您可以调整以每 100k 行或其他内容重新创建连接)。
  • 其实,没有。我没试过。我绝对同意这不是很好地利用资源,但我会稍微尝试一下。我无法想象人们会忍受这样的事情。所以,它仍然让我想知道我做错了什么。
  • 我刚刚试了一下。我在有和没有连接池的情况下都试过了。如果没有连接池,它会启动大量连接,并且在池化后不久它就会在达到池最大值后死亡。这两个选项都不会真正起作用:(

标签: mysql database powershell memory


【解决方案1】:

我对与 Powershell -join 运算符有关的问题的初步结论似乎是错误的。

这就是我正在做的事情。请注意,我将每一行添加到一个数组中,稍后我将在形成我的 SQL 时将其展开。 (附带说明,向数组中添加项目往往比连接字符串更高效)

$dataForInsertion =  = New-Object System.Collections.Generic.List[String]
$reader = [IO.File]::OpenText($filetoread)
$lineCount = 1
   while ($reader.Peek() -ge 0) {
      $line = $reader.Readline()
      $dataForInsertion.add($line)
      if($lineCount -ge 1000-or $reader.Peek() -lt 0) {

          insert_into_db -insertthis $dataForInsertion

          $lineCount = 0
      }
   $lineCount++
   }
$reader.Close()
$reader.Dispose()

调用插入函数:

   sql_query -query "SET autocommit=0;INSERT INTO ``$table`` ($columns) VALUES $($dataForInsertion -join ',');COMMIT;"

改进后的插入函数现在如下所示:

function insert_into_db {
    $command.CommandText = $query                           # Load query into object
    $script:RowsInserted = $command.ExecuteNonQuery()       # Execute command
    $command.Dispose()                                      # Dispose of command object
    $query = $null
}

所以,原来我对问题根源的初步结论是错误的。 Powershell -join 运算符与该问题无关。

在我的 SQL 插入函数中,我在每次插入时都重复调用 $connection.CreateCommand()。一旦我将它移入处理设置连接的函数(仅调用一次 - 或在需要时),内存泄漏就消失了。

【讨论】:

  • 在这种用法中,-join 也会让您面临 SQL 注入类型的问题,因为您没有正确处理字符串 - 最好的情况是,您会得到无效的 SQL。最坏的情况是,文件中的数据会导致数据从数据库中删除。您应该改为使用bulk data import - 它可能会比您在此处执行的操作快
  • 我当然同意 SQL 注入的可能性。在这种特殊情况下,我专门控制数据并在插入之前彻底清理它。关于批量导入的问题 - 它是否能够跨不可靠的连接处理大型导入作业?大约有 30+ 百万条记录,还是需要批量?
  • 我从来没有做过 mysql 批量导入,更不用说那个大小的了。如果 mysql 在这种情况下无法优雅地处理不可靠的连接,我肯定会建议将其拆分为更小的批次,或者将文件传输到连接更稳定的位置并从那里运行导入。
猜你喜欢
  • 2013-07-09
  • 2018-12-22
  • 1970-01-01
  • 1970-01-01
  • 2014-12-11
  • 1970-01-01
  • 2012-12-28
  • 2017-08-31
  • 2018-03-24
相关资源
最近更新 更多