【问题标题】:Symfony : Doctrine data fixture : how to handle large csv file?Symfony:Doctrine 数据夹具:如何处理大型 csv 文件?
【发布时间】:2016-06-17 23:07:32
【问题描述】:

我正在尝试使用理论数据夹具从“大型”CSV 文件(3Mo/37000 行/7 列)插入(在 mySQL 数据库中)数据。

这个过程很慢,此时我无法成功(可能我不得不再等一会儿)。

我想学说数据固定装置不是为了管理如此大量的数据?也许解决方案应该是直接将我的 csv 导入数据库?

知道如何进行吗?

代码如下:

<?php

namespace FBN\GuideBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use FBN\GuideBundle\Entity\CoordinatesFRCity as CoordFRCity;

class CoordinatesFRCity extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        $csv = fopen(dirname(__FILE__).'/Resources/Coordinates/CoordinatesFRCity.csv', 'r');

        $i = 0;

        while (!feof($csv)) {
            $line = fgetcsv($csv);

            $coordinatesfrcity[$i] = new CoordFRCity();
            $coordinatesfrcity[$i]->setAreaPre2016($line[0]);
            $coordinatesfrcity[$i]->setAreaPost2016($line[1]);
            $coordinatesfrcity[$i]->setDeptNum($line[2]);
            $coordinatesfrcity[$i]->setDeptName($line[3]);
            $coordinatesfrcity[$i]->setdistrict($line[4]);
            $coordinatesfrcity[$i]->setpostCode($line[5]);
            $coordinatesfrcity[$i]->setCity($line[6]);

            $manager->persist($coordinatesfrcity[$i]);

            $this->addReference('coordinatesfrcity-'.$i, $coordinatesfrcity[$i]);


            $i = $i + 1;
        }

        fclose($csv);

        $manager->flush();
    }

    public function getOrder()
    {
        return 1;
    }
}

【问题讨论】:

    标签: php mysql symfony csv doctrine-orm


    【解决方案1】:

    当您像这样创建大批量导入时要遵循两个规则:

    • 禁用 SQL 日志记录:($manager-&gt;getConnection()-&gt;getConfiguration()-&gt;setSQLLogger(null);) 以避免大量内存丢失。

    • 经常冲洗和清除,而不是最后一次。我建议您在循环中添加 if ($i % 25 == 0) { $manager-&gt;flush(); $manager-&gt;clear() },以每 25 次 INSERT 刷新一次。

    编辑:我忘记的最后一件事:当你不再需要实体时,不要将它们保存在变量中。在这里,在您的循环中,您只需要正在处理的当前实体,因此不要将先前的实体存储在 $coordinatesfrcity 数组中。如果继续这样做,可能会导致内存溢出。

    【讨论】:

    • 谢谢。我曾尝试在每次插入时冲洗,但我想这是一个太重要的频率。我会试试你的提议。抱歉,什么是 SQL 日志记录?
    • Doctrine 带有一个日志系统,可以跟踪执行的 SQL 查询。在这种情况下,您将不需要它,这将是无用的内存成本。
    • 好的,我了解什么是 SQL 日志记录。你是对的,这个表来自另一段代码,在这里没有必要。
    • 完美运行。在我的 linux PC (LAMPP) 上完成加载大约需要一分钟。
    • 简直难以置信。我正在导入具有 14 列和 453000 行(200Mo)的 CSV。该脚本花了 48 小时来导入所有行。通过这种优化,只需 10 分钟。这真是太棒了。非常感谢!
    【解决方案2】:

    文档中有一个很好的例子:http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html

    使用取模(x % y)表达式实现批处理,本例一次插入20个。您可以根据您的服务器对此进行优化。

    $batchSize = 20;
    for ($i = 1; $i <= 10000; ++$i) {
        $user = new CmsUser;
        $user->setStatus('user');
        $user->setUsername('user' . $i);
        $user->setName('Mr.Smith-' . $i);
        $em->persist($user);
        if (($i % $batchSize) === 0) {
            $em->flush();
            $em->clear(); // Detaches all objects from Doctrine!
        }
    }
    $em->flush(); //Persist objects that did not make up an entire batch
    $em->clear();
    

    【讨论】:

    • 谢谢。这与Sogara的答案相同。我会尽快尝试的。
    • 前几天我自己在文档页面上遇到了类似的问题,只是想我会给你一个我手头的来源。
    • 完美!请参阅我对 Sogara 答案的评论。
    【解决方案3】:

    对于需要大量内存但不相互依赖的固定装置,我通过使用附加标志一次插入一个实体(或较小的一组实体)来解决这个问题:

    bin/console doctrine:fixtures:load --fixtures="memory_hungry_fixture.file" --append
    

    然后我编写了一个 Bash 脚本,它可以根据需要多次运行该命令。

    在您的情况下,您可以扩展 Fixtures 命令并使用一个标志来执行批量实体 - 前 1000 行,然后是第二 1000 行,等等。

    【讨论】:

      猜你喜欢
      • 2017-07-05
      • 2015-10-10
      • 2015-02-20
      • 2021-04-17
      • 2018-01-18
      • 2012-05-30
      • 2021-09-20
      • 1970-01-01
      相关资源
      最近更新 更多