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,84 МБ
  • Спеціальний код: 17,12 секунди, використано 15,45 МБ

Тож для видалення 100 продуктів мій спеціальний код швидше на 3 секунди і менше на 0,4 МБ.

Тест 2: Використання delete()методу збирання

Я замінив оригінальний код цим:

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

І розум, вибуханий ось такі результати:

  • Оригінальний код: 19,97 секунди, використано 15,84 МБ
  • Спеціальний код: 1,24 секунди, використано 6,34 Мб

Тож для видалення 100 продуктів мій спеціальний код швидше на 18 секунд і менше на 9 Мб.

Як зазначено в коментарях, схоже, що цей метод не запускає події Magento (після завантаження, після видалення), а також індекс / кеш-пам'ять.

Питання

Отже, моє запитання: чи є причина, чому основна команда Magento не використала метод walk('delete')збирання або подію краще, delete()а не завантажувати продукти в цикл (який ми всі знаємо, дуже погана практика)?

Головною метою є усвідомлення таких ключових моментів у разі розробки модуля: чи є конкретні випадки, коли не можна використовувати метод walk/ collection delete()?

EDIT: причина, безумовно, не в тому, що catalog_controller_product_deleteподія розсилається, оскільки один і той же код можна знайти в декількох місцях (перевірити massDeleteметоди) в Magento core. Я використав приклад продуктів, щоб виділити продуктивність, оскільки вони, як правило, є найбільшими об'єктами


3
Я думаю, це через подію. Але я згоден з вами, це поганий стиль, особливо використання getSingleton()як міри продуктивності замість очевидного використання колекції. О, і можливо, ініціювати подію також колекцією, тільки не walk()ярликом.
Фабіан Шменглер

1
@fschmengler Так, я думав і про подію, але, як я вже говорив у своїй редакції, це відбувається в багатьох місцях, куди жодна подія не надсилається.
Рафаель в Digital Pianism

3
Не дивно. delete()робить запит DELETE замість завантаження колекції та видалення кожного продукту. З цим ви дійсно втратите події.
Фабіан Шменглер

5
@fschmengler Видалення колекції також видаляє для кожного окремого елемента, але воно обходить очищення кешу та ініціює деякі події magento та індексатора. Ось звідки повинна виходити різниця.
Вінай

2
@Vinai ви праві. Бажання подумати на моєму боці
Фабіан Шменглер

Відповіді:


4

Тож я зробив кілька тестів на ефективність, додав кілька дзвінків із реєстрації, щоб перевірити витрачений час та використання пам'яті для видалення 100 продуктів

Бічна примітка, але ви повинні подивитися на використання Varien Profiler для цього!

мій спеціальний код швидше на 2 секунди і менше на 0,4 МБ

Хоча я не сумніваюся, що ваша зміна покращить ефективність, було б корисно надати результати "до", щоб порівняти покращення.

Чи є причина, чому основна команда Magento не використовувала walk('delete')замість того, щоб завантажувати продукти у цикл (що всі ми знаємо, це дуже погана практика)?

Що ж, з інших питань на цьому форумі ми знаємо наступне:

  • Кодова база Magento розвивалася і розвивалася протягом багатьох років
  • Над ним працювало багато розробників
  • Процеси робочого процесу Magento з розробки основних процесів значно покращилися за час роботи над платформою, наздоганяючи сучасні найкращі практики та методи до того моменту, як Magento 2 зараз демонструє багато провідних сучасних практичних програм дизайну додатків

Тому я б припустив, що приклад, який ви знайшли, - це, мабуть, один із потенційно багатьох дорогоцінних каменів, прихованих у коді, написаних або давно, і / або менш досвідченим розробником. Як і велика частина основного коду (та коду спільноти!), Він був би протестований на невеликому наборі даних, а не бойовий, тому ефективність можливо не ретельно контролювалася.

Чи є ваше вдосконалення корисним та більш узгодженим із найкращою практикою, ніж оригінальний код? Так. Ви, як розробник Magento [1.x], однак не маєте можливості внести запропоновані вдосконалення, хоча, як і ви з Magento 2, тому моєю пропозицією буде впровадити це в локальний модуль, якщо вам це потрібно для роботи в одному з ваших магазинів або ігноруйте це, якщо це не впливає на вас, але ви помітили це, роблячи деякі дослідження.

Як оновлення редагування вашого запитання, я впевнений, що ви знаєте, що метод прогулянки в Varien_Data_Collection приймає довільний зворотний дзвінок, тож ви можете використовувати його для всього, що ви хотіли б. Для відправки події в оригінальному прикладі ви можете це зробити за допомогою функції прогулянки, а також видалити.

Єдиною причиною, яку я міг уявити, що завантаження продукту перед його видаленням буде корисним, може бути те, що спостерігачам, приєднаним до цієї події, може знадобитися повний набір даних, не доступний без попереднього завантаження продукту. Якщо це так, це пояснило б, чому вони використовують одиночну, а не модель, щоб принаймні мінімізувати накладні витрати на об'єкт.


Дякую, що я додав до та після результатів результати. Отже, ви думаєте, що немає жодної конкретної причини, крім того, що це старий код?
Рафаель в цифровому піанізмі

2
Це я б здогадався, так. Завантаження продукту перед його видаленням не послужило б іншим, ніж розпалювати події завантаження, які не мають значення для видалення. Зазвичай ви завантажуєте продукт, щоб отримати його повний набір даних, який може знадобитися одному з спостерігачів, приєднаних до події - якщо це так, то це пояснить, чому вони використовують одиночну кнопку замість моделі.
Роббі Аверилл

1
Перегляньте мою редагування з додатковими тестами, результати ще більш
шалені

0

Я думаю, що вони роблять це, щоб запустити catalog_controller_product_deleteподію, яку використовує Mage_Tag.

catalog_product_delete_beforeабо catalog_product_delete_afterозначало б, що це було непотрібно, хоча я б подумав. Цікаво, чи ця конкретна подія також використовується для реєстрації дій адміністратора.


Думка про те, що занадто , але це безумовно не причина , як це і буває в massDelete()діїCustomerController.php
Рафаель в Digital піанізму

Перегляньте мою редагування з додатковими тестами, результати ще більш
шалені

0

Я думаю, що масове видалення має працювати як видалення одного (повністю завантаженого) продукту.

Бо $collection->delete()відповідь вже дана. Якщо ви не спрацьовуєте deleter_before, delete_afterя міг би зламати деякі розширення і обійшов би деяких спостерігачів, які використовуються в ядрі.

$collection->walk('delete')Можливо, це спрацює, але все-таки є недоліком того, що дані про продукт не є повними. Це також може зламати нестандартних спостерігачів, якщо вони покладаються на додаткові дані, наприклад, об'єкт запасу.

Я думаю, якщо ви зміните ->addAttributeToSelect('entity_id')до ->addAttributeToSelect('*')і додати ->setFlag('require_stock_items', true)(додати дані про запаси в продукти) не працюватиме краще , ніж «петля видалення».

Виглядає як поганий стиль, але я думаю, що він підходить для обох дій масового видалення.

Я також використовую walk()і delete()для користувацьких моделей, але я знаю, що спостерігачів немає або entity_idдостатньо. Зазначимо, він walk()би працював з усіма подіями, які використовуються в ядрі, тому що вони використовуються лише $product->getId(), але ви не знаєте про сторонніх спостерігачів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.