我在创建主要实体while
的地方有一个Order
循环,这个实体有很多字段和关联但是持续快速,非常快。
但它作为一个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
个Item
创建了Item
,但由于Item
对象太小,我觉得它不需要花那么长时间......
使用forEach
manyToMany:
items:
targetEntity: AppBundle\Entity\Item
cascade: ["all"]
joinTable:
name: order_has_item
joinColumns:
product_id:
referencedColumnName: id
inverseJoinColumns:
item_id:
referencedColumnName: id
,结束脚本大约需要35分钟。如果我删除它,脚本只需要4分钟就可以运行,这很荒谬......
关系映射如下
Item
它是单向的,因此不会创建Applications does not run in background
的映射。
答案 0 :(得分:3)
你正在生成53000 Order
个,其中每个都有(如你所说)大约17 Item
s。这是生成的954000个数据库条目。
您每隔200 Order
持续一次,因此每次调用$em->persist()
都会创建3600个实体对象和数据库条目。
至少有两次性能点击:
此外,请不要忘记,如果您在dev
环境中执行此操作,Symfony将记录大量内容(请参阅app/logs/dev.log
)并具有多个调试功能。< / p>
那么,你如何将近百万个条目推入你的数据库?不幸的是,在真正的批量操作中,Doctrine非常糟糕。使用本机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以了解有关原生查询的更多信息。
顺便说一下,在您的示例中,如果您只在($i % 200 == 0)
时写入数据库,那么您将丢失($i % 200)
最后一项。你需要写一些类似的东西:
$amount = 53000;
while($i < $amount)
{
// do your thing
$i++;
if($i % 200 === 0 || $i >= $amount)
{
// flush/clear
}
}