Magento 1:エンティティを削除するためのパフォーマンス最適化


10

現在、パフォーマンスに関していくつかのモジュールを改善しようとしています。

コレクションでのメソッド使用法を知っている方もいるかもしれません。これは、製品を直接ループするのを回避するのに非常に役立ちます。walk()

さらに、@ Vinaiのおかげで、コレクション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')、コレクションdelete()メソッドを使用しない理由がありますか?

主な目標は、モジュール開発の場合は、そのようなキーポイントに注意することがある:1を使用することはできませんいずれかの特定の例があるwalk/収集delete()方法は?

編集:理由は間違いなくcatalog_controller_product_delete同じコードがmassDeleteMagentoコアのいくつかの場所(メソッドをチェック)で見つけることができるので、イベントがディスパッチされているためではありません。製品の例を使用して、通常は最大のエンティティであるため、パフォーマンスを強調しました


3
イベントのせいだと思います。しかし、私はあなたに同意します。それgetSingleton()は、明らかなコレクションの使用法ではなく、特にパフォーマンス測定としての使用法は悪いスタイルです。walk()ショートカットではなく、コレクションを使用してイベントをトリガーすることもできます。
Fabian Schmengler 2016年

1
@fschmenglerええ、私もイベントについて考えましたが、編集で言ったように、イベントがディスパッチされない多くの場所で起こっています。
デジタルピアニズムのラファエル、

3
驚かない。delete()コレクションをロードして各製品を削除する代わりに、DELETEクエリを作成します。それであなたは本当にイベントを失うでしょう。
Fabian Schmengler 2016年

5
@fschmenglerコレクションの削除では、個々のアイテムも削除されますが、キャッシュのクリアと一部のmagentoおよびインデクサーイベントのトリガーはバイパスされます。それが違いが生じなければならないところです。
ビナイ

2
@Vinaiあなたは正しい。私の側の希望的な思考
ファビアンシュメングラー2016年

回答:


4

そこで、いくつかのパフォーマンステストを行い、100個の製品を削除した場合の所要時間とメモリ使用量を確認するためのロギング呼び出しをいくつか追加しました

余談ですが、Varienプロファイラの使用を検討する必要があります。

私のカスタムコードは2秒高速で、使用するメモリは0.4 MB少なくなっています

変更によってパフォーマンスが向上することは間違いありませんが、「前」の結果を提供して改善を比較すると便利です。

Magentoコアチームがwalk('delete')製品をループでロードする代わりに使用しなかった理由はありますか?

まあ、私たちはこのフォーラムの他の質問から次のことを知っています:

  • Magentoコードベースは長年にわたって開発され、進化してきました
  • 多くの開発者が取り組んでいます
  • Magentoコア開発ワークフロープロセスは、プラットフォームで作業を行っている間に劇的に改善され、Magento 2が現在多くの主要な最新のアプリケーション設計プラクティスを示すようになるまで、最新のベストプラクティスとテクニックに追いついています。

だから私はあなたが見つけた例がおそらくずっと前に、または経験の少ない開発者によって書かれたコードに隠された潜在的に多くの宝石の1つであることをお勧めします。コアコード(およびコミュニティコード!)の多くと同様に、小規模なデータセットでテストされ、戦闘テストされていなかったため、パフォーマンスは厳密に監視されていなかった可能性があります。

改善は有益であり、元のコードよりもベストプラクティスに密接に一致していますか?はい。コミュニティMagento [1.x]開発者としてのあなたは、Magento 2の場合とは異なり、提案された改善に貢献することができないため、いずれかのストアでのパフォーマンスに必要な場合、これをローカルモジュールに実装することをお勧めします、または影響がない場合は無視してください。ただし、調査中に気づいた場合は、

質問の編集の更新として、Varien_Data_Collectionのwalkメソッドが任意のコールバックを受け入れることを知っていると思います。したがって、おそらくあなたが望むものに自由に使用できます。元の例のイベントをディスパッチする場合は、削除と同様にwalk関数を使用してそれを行うことができます。

削除する前に製品をロードすることが役立つと私が想像できる唯一の理由は、そのイベントにアタッチされたオブザーバーが、最初に製品をロードしないと使用できない完全なデータセットを必要とする場合があるためです。その場合は、少なくともオブジェクトのオーバーヘッドを最小限に抑えるために、モデルではなくシングルトンを使用する理由を説明します。


投稿に前後の結果を追加してくれてありがとう。古いコードであるということ以外に特別な理由はないと思いますか?
デジタルピアニズムのラファエル、

2
そうですね。削除する前に製品をロードしても、削除には関係のないロードイベントを発生させる以外のメリットはありません。通常、製品をロードして完全なデータセットを取得します。これは、イベントに関連付けられたオブザーバーの1つに必要な場合があります。その場合は、モデルではなくシングルトンを使用している理由を説明します。
ロビーアベリル

1
より多くのテストで私の編集を参照してください。結果はさらに奇妙です
Digital PianismでのRaphael

0

私の考えでは、catalog_controller_product_deleteMage_Tagによって使用されているイベントを発生させるために彼らがやっていると思います。

catalog_product_delete_beforeまたはcatalog_product_delete_after、私が思うに、これは不要でした。この特定のイベントが管理アクションのロギングにも使用されているのではないでしょうか。


あまりにもそのことについて考えたが、それはまたのために起こるようにそれは間違いの理由ではないmassDelete()のアクションCustomerController.php
デジタルPianismでラファエル

より多くのテストで私の編集を参照してください。結果はさらに奇妙です
Digital PianismでのRaphael

0

一括削除は、単一の(完全にロードされた)製品を削除するように機能するはずです。

$collection->delete()答えすでに与えられています。トリガーしないとdeleter_beforedelete_after拡張機能が壊れる可能性があり、コアで使用されているオブザーバーをバイパスします。

$collection->walk('delete')は機能する可能性がありますが、製品データが完全ではないという欠点があります。また、カスタムオブザーバーが在庫アイテムオブジェクトなどの追加データに依存している場合も、カスタムオブザーバーが機能しなくなります。

変更->addAttributeToSelect('entity_id')->addAttributeToSelect('*')て追加した場合->setFlag('require_stock_items', true)(製品に在庫データを追加した場合)、「ループ削除」よりもパフォーマンスが低下すると思います。

悪いスタイルのように見えますが、両方の一括削除アクションに適していると思います。

私はカスタムモデルにもを使用walk()delete()ていますが、オブザーバーがいない、またはentity_id十分であることを知っています。ただ言及すると、walk()コアで使用されるすべてのイベントで動作します。それらはのみを使用するため$product->getId()ですが、サードパーティのオブザーバーについては知りません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.