Magento 1: использование Mage::объекты() для кэширования моделей


Итак, я выяснил, что теоретически Mage::objects() можно использовать для кэширования объектов с помощью Varien_Object_Cache.

Я заметил, что если я выполню следующее:

// Test #1
for ($i = 0; $i < 100000; $i++) {
    $product = Mage::getModel('catalog/product');
}

// Test #2
$productModel = Mage::getModel('catalog/product');
$cache = Mage::objects()->save($productModel);

for ($i = 0; $i < 100000; $i++) {
    $product = Mage::objects()->load($cache);
}

Производительность (как во времени, так и в памяти) сумасшедшая: первый цикл занимает почти в 20 раз больше времени, чем второй.

Итак, вот что меня беспокоит, допустим, у меня есть цикл, который импортирует много подобных продуктов:

foreach($datas as $data) {
    $product = Mage::getModel('catalog/product');
    $product->addData($data);
    $product->save();
}

Теперь что, если я добавил следующее в свой класс:

protected $_productModel;

public function getProductModel()
{
    if (is_null($this->_productModel)) {
        $productModel = Mage::getModel('catalog/product');
        $this->_productModel = Mage::objects()->save($productModel);
    }
    return Mage::objects()->load($this->_productModel);
}

В моем цикле, допустим, я заменяю:

$product = Mage::getModel('catalog/product');

С помощью:

$product = $this->getProductModel();

Таким образом, идея, о которой я говорю, заключалась бы в том, чтобы выделить класс продукта в пул объектов из Varien_Object_Cache (доступный через Mage::objects()), чтобы объект из getModel не нужно было выделять и уничтожать снова и снова.

  • Какие риски это подразумевает?
  • Это правильный способ использования Mage::objects() ?
  • Каковы пределы Varien_Object_Cache, например, могу ли я кэшируйте больше моделей, таких как Mage::getModel('catalog/category') ?
Author: Raphael at Digital Pianism, 2016-09-29

1 answers

Я должен признать, что это первый раз, когда я увидел Mage::objects() и Varien_Object_Cache, но после просмотра кода я думаю, что это было хорошо. Это должно оставаться в неведении. В Magento 1 уже слишком много глобального состояния.

Во-первых, я согласен с Кевином Шредером в том, что вы сравниваете яблоки с апельсинами. Varien_Object_Cache - это специальная форма реестра, она сохраняет экземпляры объектов и возвращает их. Это не сохраняет или возвращает копию (что вы, кажется, подразумеваете)

В отличие от Mage::getSingleton(), очевидно, заключается в том, что вы можете сохранять множество объектов одного и того же класса, а отличие от Mage::registry() заключается в том, что вам не нужно выбирать ключ, новый ключ будет назначен автоматически.

Однако есть несколько интересных особенностей:

  • Если вы сохраните один и тот же объект дважды, одна и та же запись в кэше будет использована повторно, поэтому она находится в кэше только один раз (идентификация определяется spl_object_hash, и именно здесь она становится ошибочной. Я вернусь к этому позже)
  • Вы можете добавляйте теги кэша при сохранении объекта и находите или удаляйте объекты по тегу
  • Вы также можете находить или удалять объекты по классу

О spl_object_hash()

У меня всегда возникают подозрения, если люди пытаются быть умными с этой функцией. У него есть одна ловушка: он основан на некотором внутреннем идентификаторе, и как только объекты будут собраны в мусор, новые объекты получат тот же идентификатор. Поэтому, если сравнивать два значения spl_object_hash , имеет смысл только в том случае, если оба объекта находятся в памяти одновременно. Похоже, здесь это не гарантируется, по крайней мере, я не вижу, чтобы записи всегда удалялись из массива хэшей, когда соответствующий объект удаляется из кэша.

С этим кодом

Mage::objects()->save($obj);
Mage::objects()->delete($obj);

Объект будет удален из кэша, но "хэш" все еще там. Вот var_dump после того, как я добавил три объекта и удалил два:

class Varien_Object_Cache#827 (9) {
  protected $_idx =>
  int(3)
  protected $_objects =>
  array(1) {
    '#3' =>
    class stdClass#883 (1) {
      public $baz =>
      string(3) "baz"
    }
  }
  protected $_hashes =>
  array(3) {
    '00000000403b4ef2000000002079cc52' =>
    string(2) "#1"
    '00000000403b4eff000000002079cc52' =>
    string(2) "#2"
    '00000000403b4eb9000000002079cc52' =>
    string(2) "#3"
  }
  protected $_objectHashes =>
  array(3) {
    '#1' =>
    string(32) "00000000403b4ef2000000002079cc52"
    '#2' =>
    string(32) "00000000403b4eff000000002079cc52"
    '#3' =>
    string(32) "00000000403b4eb9000000002079cc52"
  }
  protected $_tags =>
  array(0) {
  }
  protected $_objectTags =>
  array(0) {
  }
  protected $_references =>
  array(0) {
  }
  protected $_objectReferences =>
  array(0) {
  }
  protected $_debug =>
  array(0) {
  }
}

Если вы посмотрите внимательно, вы также должны понять, почему я использовал кавычки вокруг "хэша". Значения имеют только совсем небольшие различия.

В документации для spl_object_hash() явно указано:

Когда объект уничтожается, его хэш может быть повторно использован для других объектов.

Комментарии лучших пользователей также актуальны:

Обратите внимание, что содержимое (свойства) объекта НЕ хэшируется функцией, а только ее внутренним дескриптором и указателем таблицы обработчика. Этого достаточно, чтобы гарантировать, что любые два объекта одновременно совместное проживание в память будет иметь разные хэши. Уникальность не гарантируется между объектами, которые не находились в памяти одновременно, например:

var_dump(spl_object_hash(new stdClass()), spl_object_hash(new  stdClass()));

Запуск только этого, как правило, генерирует одни и те же хэши, так как PHP повторно использует внутренний дескриптор для первого стандартного класса после того, как он был разыменован и уничтожен при создании второго стандартного класса.

И

Обратите внимание, что для двух разных объектов spl_object_hash() может возвращать значения, которые выглядят очень похоже, и на самом деле оба наиболее значимых и наименьшие значащие цифры, скорее всего, будут идентичными! например, "000000003cc56d770000000007fa48c5" и "000000003cc56d0d0000000007fa48c5".

Заключение:

Не используйте его. Это не так полезно, как вы могли бы подумать, и если вы хотите сэкономить память, повторно используя объекты, вы можете сделать это лучше (более производительно, без глобального состояния и без ошибок!).

Например, ваш код эквивалентно

$product = Mage::getModel('catalog/product');
foreach($datas as $data) {
    $product->addData($data);
    $product->save();
}

Код, который вы связали с mage_catalog_model_convert_adapter_product, с таким же успехом мог быть:

    if (is_null($this->_productModel)) {
        $this->_productModel = Mage::getModel('catalog/product')
    }
    return $this->_productModel;

Ключ, возвращенный из кэша объектов, больше нигде не используется, так зачем помещать его туда, чтобы он был доступен глобально? Разработчик, вероятно, также думал, что будет происходить какая-то волшебная оптимизация.

Достаточно интересно, что я не нахожу никакого использования в основном коде специальных функций (тег кэша, удаление по модели), только бесполезно код "сохранить и загрузить в том же месте", как в приведенном выше примере.

Итак, похоже, что вы нашли еще один случай исторического кода, который на самом деле никогда не использовался.

 5
Author: Fabian Schmengler, 2017-04-13 12:55:04