doctrine2 - 如何提高冲洗效率?

时间:2012-01-03 03:05:42

标签: php mysql doctrine-orm

我必须更新我的Doctrine实体以匹配(potentionaly very large)XML文件中的记录。我还必须根据XML中的数据更新ManyToMany关联。这就是我在循环中所做的事情:

  1. 从XML获取数据
  2. 从DB获取实体(如果不存在则创建新的)
  3. 设置新的实体属性
  4. 获取当前实体关联(getter返回ArrayCollection对象)
  5. 清除所有关联(通过调用ArrayCollection::clear()
  6. 设置新关联(通过在子循环中调用ArrayCollection::add()
  7. 通过EntityManager保留实体
  8. 循环之后,我致电EntityManager::flush()

    问题是刷新会产生大量查询,而不是一次更新/插入/删除多行。对于每个实体在查询后执行:

    • SELECT从DB获取实体
    • UPDATE更新实体属性(现在实际上已跳过,因为没有更改属性......)
    • 删除以清除以前的关联
    • INSERT以插入新关联

    因此,对于XML中的305条记录,我总共获得了915条查询(我想如果所有实体都会更改,最多可能会有1220条查询),这会导致导入非常慢。

    我可以在循环之前利用IdentityMap和预取实体,但仍然有UPDATE / DELETE / INSERT查询。

    • 有没有办法让flush方法更好地优化查询(使用多插入,WHERE IN而不是多个DELETE查询等)?
    • 这是冲洗方法的正常行为还是我做错了什么?
    • 也许我更新实体关联的方式存在问题。有更好的方法如何做到这一点? (而不是“获取/清除/添加”方法)
    • 我知道Doctrine不适用于大规模投注处理,但我认为将其用于XML导入是如何避免可能出现的非ORM方法的DB不一致的最佳方法。是吗?
    • 如果上述方法有误,我应该如何解决问题?

2 个答案:

答案 0 :(得分:32)

你做得对 - 它只是很慢,因为增加的ORM抽象意味着你不能进行你想要的各种优化。

也就是说,EntityManager在大的事务上确实变慢了。如果你在一个大事务中并不是绝对需要它们,你可以通过flush()获得更高性能的代码,然后每20-200次迭代循环清除()EM。

如果这不能提供足够的性能,我能想到的唯一选择是恢复到直接针对您的DBMS运行自定义SQL的自定义代码。

我知道这不是一个好的答案,但至少我可以告诉你,你并不疯狂。

------编辑------

来自Batch processing上的官方Doctrine2文章:

  

有些人似乎在想为什么Doctrine不使用   多插入(插入(...)值(...),(...),(...),...

     

首先,只有mysql和更新版本支持此语法   postgresql版本。其次,没有简单的方法来掌握所有   使用时在这样的多插入中生成的标识符   AUTO_INCREMENT或SERIAL和ORM需要标识符以进行标识   管理对象。最后,插入性能很少   ORM的瓶颈。普通插入物的速度足够快   大多数情况下,如果你真的想做快速批量插入,那么a   多插入不是最好的方式,即Postgres COPY或Mysql   LOAD DATA INFILE快几个数量级。

     

这就是为什么不值得努力实施的原因   抽象,在mysql和postgresql上执行多插入   ORM。

使用远程与本地数据库时,性能也存在显着差异,因为将每个查询发送到远程服务器的开销非常大。由于事务和数据库优化,使用本地数据库时开销要低得多。 (例如,在问题中的示例情况下,70秒降低到300毫秒)

答案 1 :(得分:4)

不确定这是否能直接回答原始海报提出的问题,但希望这可以帮助其他人在冲洗时解决Doctrine速度问题。

...关于冲洗速度,请确保您的xdebug探测器未打开。

[php.ini]
; PROFILING
;xdebug.profiler_enable = 1
;xdebug.profiler_output_name = "cachegrind.out.%t.%s.%p"
;xdebug.profiler_output_dir = "C:\xampp\tmp"

作为一个示例,在我的情况下,这对Doctrine刷新操作有多大影响,3000条记录为55秒,而关闭探测器则为5秒!