【问题标题】:How can I improve CSV import faster in Symfony?如何在 Symfony 中更快地改进 CSV 导入?
【发布时间】:2020-11-09 07:53:55
【问题描述】:

我必须导入大约 20,000 行的 CSV 文件。此 CSV 是从 FTP 服务器读取的,该服务器每 30 分钟更新一次。但是我编写的代码已经花费了超过 45 分钟的时间来导入。这是非常缓慢的。谁能帮帮我。

foreach ($readers as $key => $row) {
    $totalRecords +=1;

    $filterArray = $this->entityManager->getRepository(Article::class)->findBy(['id' =>  $row['id']]);

    if (empty($filterArray)) {
        $notFoundRecords +=1;
        continue;
    }
    $foundRecords +=1;
    $this->processPriceRow($row);
}


protected function processPriceRow($row)
{
    $existingRecord = $this->entityManager
                           ->getRepository(WareHouse::class)
                           ->findBy(['id' => $row['product_id']]);

    if (empty($existingRecord)) {
        return $this->fillArticleWareHouse($row);
    }
}


protected function fillArticleWareHouse($row, $i, $batchSize)
{
    $newWareHouse = new WareHouse();
    ....
    ....
    ...

    // Insert.
    $this->entityManager->persist($newWareHouse);
    $this->entityManager->flush();
}

我正在考虑根据 batchSize = 100 来保存数据。但由于我在函数内部有函数,所以我也无法实现它。

【问题讨论】:

  • 你有没有试过在每个实体持久化后不运行flush?这将有很大帮助

标签: php symfony4 csv-import


【解决方案1】:

你可以像这样实现批处理。


    protected $batchSize = 100;
    protected $i = 0;
    
    protected function processPriceRow($row)
    {
        $existingRecord = $this->entityManager
            ->getRepository(WareHouse::class)
            ->findBy(['id' => $row['product_id']]);

        if (empty($existingRecord)) {
            return $this->fillArticleWareHouse($row);
        }
        $this->entityManager->flush();
    }

    protected function fillArticleWareHouse($row)
    {
        $newWareHouse = new WareHouse();
        //....
        $this->entityManager->persist($newWareHouse);
        ++$this->i;
        if (($this->i % $this->batchSize) === 0) {
            $this->entityManager->flush();
        }
    }

此外,如果您一次选择所有 Article 和 WareHouse 实体并将它们保存到数组 [entityId => Entity] 中会更好。

        // articles
        $rowIds = [];
        foreach ($readers as $key => $row) {
            $rowIds[] = $row['id'];
        }
        
        $articles = $this->entityManager->getRepository(Article::class)->findBy(['id' =>  $rowIds]);
        $articleIdToArticle = [];
        foreach ($articles as $article) {
            $articleIdToArticle[$article->getId()] = $article;
        }
        
        foreach ($readers as $key => $row) {
            $totalRecords +=1;
            if(!key_exists($row['id'], $articleIdToArticle)) {
                $notFoundRecords +=1;
                continue;
            }            
            $foundRecords +=1;
            $this->processPriceRow($row);
        }

【讨论】:

  • if (($this->i % $this->batchSize) === 0) { $this->entityManager->flush();这会刷新所有记录吗?我不需要检查是否有记录需要刷新
  • 所有持久化的实体(100x WareHouse)都会被刷新到那里。你不需要检查是否有记录。
猜你喜欢
  • 1970-01-01
  • 2013-10-15
  • 1970-01-01
  • 2018-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-23
  • 1970-01-01
相关资源
最近更新 更多