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')
?
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;
Ключ, возвращенный из кэша объектов, больше нигде не используется, так зачем помещать его туда, чтобы он был доступен глобально? Разработчик, вероятно, также думал, что будет происходить какая-то волшебная оптимизация.
Достаточно интересно, что я не нахожу никакого использования в основном коде специальных функций (тег кэша, удаление по модели), только бесполезно код "сохранить и загрузить в том же месте", как в приведенном выше примере.
Итак, похоже, что вы нашли еще один случай исторического кода, который на самом деле никогда не использовался.