2015-09-03 86 views
0

我有一个while循环在哪里我创建主要实体Order,这个实体有很多字段和关联,但坚持快速,非常快。Doctrine2协会坚持缓慢地狱

但它作为一个oneToMany单向(manyToMany)关联,这需要永远......这个关联很小,只有两个字段,所以我不知道为什么需要这么长时间。我有一个$items,正好17项的数组。在while循环中,foreach Order我在assoc里添加了17个条目。

while($i < 53000) 
{ 
    // ... 
    foreach($item as $k => $v) 
    { 
     $item = new Item(); 
     $item->setName($v['name']); 
     $item->setValue($order[$v['column']]); 

     $order->addItem($item); 
    } 

    // ... 

    $em->persist($order); 

    $i++; 
    if($i % 200 == 0) // I've tried multiples values in here but everything is slow. 
    { 
     $em->flush(); 
     $em->clear(); // Commenting this, doesn't speed up things neither. 
    } 
} 

我当然知道,每Order有17 Item创建的,但作为Item对象是如此之小,我不认为它需要持续多长时间?

随着ItemforEach,结束脚本大约需要35分钟。如果我删除,脚本只需要4分钟时间运行,这是荒谬的......

的关系映射如下

manyToMany: 
    items: 
     targetEntity: AppBundle\Entity\Item 
     cascade: ["all"] 
     joinTable: 
     name: order_has_item 
     joinColumns: 
      product_id: 
      referencedColumnName: id 
     inverseJoinColumns: 
      item_id: 
      referencedColumnName: id 

它是单向的,这样可以创建为Item没有映射。

回答

2

您正在生成53000 Order s,其中每个都有(如您所说)在17 Item s左右。这就是生成的954000个数据库条目。

您将坚持每200 Order s,因此每次调用$em->persist()时都会创建3600个实体对象和数据库条目。

有,至少,两个性能点击:

  1. 学说将产生3600个单位与所有花俏。
  2. 受影响的表的数据库索引在每个插入中更新。

另外,不要忘记,如果你在dev环境这样做,Symfony的将记录的东西,一屁股负荷(见app/logs/dev.log),并有几个调试功能。

那么,如何将近一百万个条目推入您的数据库?不幸的是,很不幸在批量运营中很不好。它通常是一个更好的使用原生SQL查询(尽管牺牲便携性......但谁在乎,反正):

$em->getConnection()->executeUpdate(/*INSERT etc. …*/); 

当插入的是相互关联的对象,这是有点难度,因为我们必须保证一致性:

try 
{ 
    $conn = $em->getConnection(); 
    $conn->beginTransaction(); 

    foreach ($orders as $order) 
    { 
     $conn->executeUpdate(/*INSERT the order …*/); 
     $orderId = $conn->lastInsertId(); 

     foreach ($order->items as $item) 
     { 
      $item->orderId = $orderId; 
      $conn->executeUpdate(/*INSERT the item …*/); 
     } 
    } 

    $conn->commit(); 
} 
catch(\Exception $e) 
{ 
    $conn->rollback(); 
    throw $e; 
} 

// NOTE: the try/catch wraps *all* orders, so if one order fails *no* 
// orders will be saved. Alternatively, put the try/catch into the loop 

(我不停的代码简称可读性,你会得到的想法没有测试过,只是从我的头顶。)

看一看在Doctrine documentation了解更多关于原生查询。


顺便说一句,在你的例子,如果你只写DB时($i % 200 == 0),你会失去你的最后($i % 200)项目。你需要写这样的东西:

$amount = 53000; 

while($i < $amount) 
{ 
    // do your thing 

    $i++; 

    if($i % 200 === 0 || $i >= $amount) 
    { 
     // flush/clear 
    } 
} 
+0

我会测试你的东西。顺便说一下,在我做了最后一个$ em-> flish()之后,最后的项目被保存了。 –