Magento 1:性能优化以删除实体


10

我目前正在尝试改进有关性能的几个模块。

你们中有些人可能知道集合方法用法,walk()这对于避免直接遍历产品非常有用。

最重要的是,感谢@Vinai,我们也可以使用collection delete()方法。

但是我注意到Magento 1本机文件并不总是使用这些方法中的任何一种进行删除。

一个我见过的最糟糕的代码是massDelete()从方法app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php,其中产品在删除之前的循环加载

foreach ($productIds as $productId) {
    $product = Mage::getSingleton('catalog/product')->load($productId);
    Mage::dispatchEvent('catalog_controller_product_delete', array('product' => $product));
    $product->delete();
}

因此,我进行了一些性能测试,添加了一些日志记录调用,以检查删除100个产品所花费的时间和内存使用情况。

测试1:walk方法

我用以下代码替换了上面粘贴的原始代码:

$collection = Mage::getResourceModel('catalog/product_collection')
                        ->addAttributeToSelect('entity_id')
                        ->addIdFilter($productIds)
                        ->walk('delete');

在我糟糕的开发服务器上的结果如下(基于10个测试的平均值):

  • 原始代码:19.97秒,已使用15.84MB
  • 自定义代码:17.12秒,已使用15.45MB

因此,对于100种产品的删除,我的自定义代码加快了3秒,而占用的内存少了0.4MB。

测试2:使用收集delete()方法

我用以下代码替换了原始代码:

$collection = Mage::getResourceModel('catalog/product_collection')
                        ->addAttributeToSelect('entity_id')
                        ->addIdFilter($productIds)
                        ->delete();

记吹这里的结果:

  • 原始代码:19.97秒,已使用15.84MB
  • 自定义代码:1.24秒,已使用6.34MB

因此,对于100种产品的删除,我的自定义代码加快了18秒,而占用的内存减少了9MB。

如评论中所述,该方法似乎不会触发Magento事件(加载后,删除后),也不会触发索引/缓存刷新。

所以我的问题是:Magento核心团队为什么没有使用walk('delete')or事件更好的收集delete()方法而不是循环加载产品(我们都知道这是非常非常糟糕的做法)吗?

主要目标是在开发模块时要注意这些关键点:在某些特殊情况下,不能使用walk/收集delete()方法吗?

编辑:原因绝对不是因为catalog_controller_product_delete事件被调度,因为可以massDelete在Magento核心的多个位置(检查方法)找到同一代码。我以产品为例来强调性能,因为它们通常是最大的实体


3
我想这是因为事件。但是我同意你的看法,这是不好的风格,特别是getSingleton()作为性能度量的用法,而不是明显的集合用法。哦,也可以通过集合来触发事件,只是不能通过walk()快捷方式来触发。
Fabian Schmengler '16

1
@fschmengler是的,我也考虑过该事件,但是正如我在编辑中所说的那样,它发生在许多没有调度事件的地方。
拉斐尔(Raphael)在Digital Pianism上,2013年

3
不奇怪。delete()进行DELETE查询,而不是加载集合并删除每个产品。有了这个,您将真正松开事件。
Fabian Schmengler,

5
@fschmengler集合删除还对每个单独的项目执行删除操作,但是它绕过了清除缓存以及触发某些magento和indexer事件的过程。那就是差异的来源。
维奈

2
@Vinai,你是对的。一厢情愿的想法站在我的身边
Fabian Schmengler 2016年

Answers:


4

因此,我进行了一些性能测试,添加了一些日志记录调用,以检查删除100个产品所花费的时间和内存使用情况

旁注,但您应该为此使用Varien Profiler!

我的自定义代码快2秒,占用的内存少0.4MB

尽管我毫不怀疑您所做的更改会提高性能,但提供“之前”结果以比较这些改进将很有用。

为何Magento核心团队不使用walk('delete')而不是循环加载产品(我们都知道这是非常非常糟糕的做法)的原因?

好吧,我们从该论坛上的其他问题中知道以下内容:

  • Magento代码库经过多年的发展和发展
  • 它有很多开发人员在努力
  • Magento核心开发工作流程在平台上工作的时间已经得到了显着改善,赶上了现代最佳实践和技术,Magento 2现在展示了许多领先的现代应用程序设计实践

因此,我建议您发现的示例可能是很久以前和/或由经验不足的开发人员编写的隐藏在代码中的许多潜在宝石之一。像许多核心代码(和社区代码!)一样,它应该在较小的数据集上进行测试,而不是经过实际测试,因此性能可能尚未受到密切监视。

与原始代码相比,您的改进是否有益,并且与最佳实践更紧密地保持一致?是。您是社区Magento [1.x]开发人员,但是却无法像Magento 2一样做出建议的改进,因此,我的建议是,如果您需要在某个商店中提高性能,则可以在本地模块中实现此功能,或者如果它没有影响您,但是您在进行研究时注意到了它,则可以忽略它。

作为问题编辑的更新,我确定您知道Varien_Data_Collection中的walk方法可以接受任意回调,因此您可以自由地将其用于任何您可能想要的事情。为了在原始示例中调度事件,您可以使用walk函数以及删除函数来完成。

我能想象在删除产品之前先加载产品的唯一原因可能是,附加到该事件的观察者可能需要不首先加载产品就无法获得的完整数据集。如果是这样,它将解释为什么他们使用单例而不是模型来至少最小化对象开销。


谢谢,我在帖子中添加了之前和之后的结果。因此,您认为除了旧代码之外,没有其他特殊原因吗?
拉斐尔(Raphael)在Digital Pianism上,2013年

2
那是我的猜测,是的。在删除产品之前加载产品,除了引发与删除无关的加载事件外,没有其他好处。通常,您加载产品以获取其完整的数据集,这可能是事件附带的一名观察者所需要的-如果是这种情况,它将解释为什么他们使用单例而不是模型。
罗比·阿弗里尔

1
查看我的编辑,进行更多测试,结果甚至更疯狂
数字钢琴家”的Raphael在

0

我的想法是他们正在这样做以触发catalog_controller_product_delete由Mage_Tag使用的事件。

catalog_product_delete_beforecatalog_product_delete_after尽管我认为这意味着没有必要。想知道此特定事件是否也用于管理员操作日志记录。


也对此进行了思考,但这绝对不是原因,因为它也发生massDelete()CustomerController.php
数码钢琴家Raphael在

查看我的编辑,进行更多测试,结果甚至更疯狂
数字钢琴家”的Raphael在

0

我认为,批量删除应该像删除单个(已满载)产品一样工作。

对于$collection->delete()答案已经给出。如果您不触发deleter_beforedelete_after我可能会破坏一些扩展并绕过核心中使用的一些观察者。

$collection->walk('delete')可能会起作用,但仍然具有产品数据不完整的缺点。如果定制观察员依赖其他数据(例如库存项目对象),这也可能会破坏它们。

我想,如果你改变->addAttributeToSelect('entity_id')->addAttributeToSelect('*'),并添加->setFlag('require_stock_items', true)(添加库存数据到产品),它不会更好地履行然后“循环删除”。

看起来风格很糟糕,但我认为这两种大规模删除操作都是正确的。

我也使用walk()delete()用于自定义模型,但是我知道没有观察者或entity_id足够了。只需提及,walk()它将与core中使用的所有事件一起使用,因为它们仅使用$product->getId(),但是您不了解第三方观察者。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.