2

对于我的项目之一,我需要导入一个非常大的文本文件(~ 950MB)。我在我的项目中使用 Symfony2 和 Doctrine 2。

我的问题是我收到如下错误:

Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 24 bytes)

如果我将内存限制增加到 1GB,甚至会发生错误。

我试图通过使用 XDebug 和 KCacheGrind (作为 PHPEdit 的一部分)来分析问题,但我并不真正理解这些值:(

我正在寻找一种工具或方法(快速简单,因为我没有太多时间)来找出为什么内存被分配而不是再次释放。

编辑

在这里清除一些东西是我的代码:

$handle = fopen($geonameBasePath . 'allCountries.txt','r');

        $i = 0;
        $batchSize = 100;

        if($handle) {
            while (($buffer = fgets($handle,16384)) !== false) {

                if( $buffer[0] == '#') //skip comments
                    continue;
                //split parts
                $parts = explode("\t",$buffer);


                if( $parts[6] != 'P')
                    continue;

                if( $i%$batchSize == 0 )    {
                    echo 'Flush & Clear' . PHP_EOL;
                    $em->flush();
                    $em->clear();
                }

                $entity = $em->getRepository('MyApplicationBundle:City')->findOneByGeonameId( $parts[0] );
                if( $entity !== null)   {
                    $i++;
                    continue;
                }

                //create city object
                $city = new City();

                $city->setGeonameId( $parts[0] );
                $city->setName( $parts[1] );
                $city->setInternationalName( $parts[2] );
                $city->setLatitude($parts[4] );
                $city->setLongitude( $parts[5] );
                $city->setCountry( $em->getRepository('MyApplicationBundle:Country')->findOneByIsoCode( $parts[8] ) );

                $em->persist($city);

                unset($city);
                unset($entity);
                unset($parts);
                unset($buffer);

                echo $i . PHP_EOL;


                $i++;
            }
        }

        fclose($handle);

我尝试过的事情,但没有任何帮助:

  1. 将第二个参数添加到fgets
  2. 增加 memory_limit
  3. 取消设置变量
4

3 回答 3

5

增加内存限制是不够的。导入这样的文件时,您会缓冲读数。

$f = fopen('yourfile');
while ($data = fread($f, '4096') != 0) {
    // Do your stuff using the read $data
}
fclose($f);

更新 :

使用 ORM 时,您必须了解在刷新调用之前实际上没有任何内容插入到数据库中。这意味着所有这些对象都由标记为“待插入”的 ORM 存储。只有在进行刷新调用时,ORM 才会检查集合并开始插入。

解决方案 1:经常冲洗。并且清楚。

解决方案 2:不要使用 ORM。使用普通的 SQL 命令。它们将比对象 + ORM 解决方案占用更少的内存。

于 2012-01-29T16:48:41.173 回答
0

33554432 为 32MB

更改 php.ini 中的内存限制,例如 75MB

memory_limit = 75M

并重新启动服务器

于 2012-01-29T16:31:27.667 回答
0

您应该逐行读取文件,而不是简单地读取文件。每次您阅读一行时,您都应该处理您的数据。不要试图将所有内容都放在内存中。你会失败的。这样做的原因是,虽然您可以将 TEXT 文件放在 ram 中,但您将无法同时将数据作为 php 对象/变量/whathaveyou,因为 php 本身需要更多的内存为每个其中。

我的建议是 a) 读取新行,b) 解析行中的数据 c) 创建新对象以存储在数据库中 d) 转到步骤 a,首先取消设置旧对象或重用它的内存

于 2012-01-29T18:09:27.530 回答