Перейти до вмісту
Пошук в
  • Детальніше...
Шукати результати, які ...
Шукати результати в ...

Оптимизация функций getProductAttributes и getProductOptions в модели catalog/product


Recommended Posts

Недавно обратился клиент (OcStore 1.5.5.1.2.) с просьбой посмотреть и узнать где именно тормоза на сайте. Так как, на сайте под завязку используются опции и аттрибуты, я стал грешить на них еще до проверки, проверка xhprof дала понять, что я не ошибался. Одним кэшированием тут не отделаться, так как данные магазина постоянно обновляются и механизм инвалидации кэша далек от идеала.

 

Было принято решение оптимизировать функции получения списка аттрибутов и опций. Что именно сделано: Убраны дополнительные подзапросы из цикла обработки аттрибутов и опций, так как все необходимые данные можно получить в одном запросе. Так же используются PHP указатели, что позволит обработать все данные в одном цикле. Все это позволит получить все необходимые данные за один запрос и быстро из обработать. 

 

В данном частном случае производительность увеличилась почти в 30 раз (очень много опций, значений опций и аттрибутов почти у каждого товара). 


public function getProductAttributes($product_id) {
	$product_attribute_group_data = array();

	$product_attribute_group_query = $this->db->query("SELECT a.attribute_id, ad.name as attribute_name, pa.text, ag.attribute_group_id, agd.name as attribute_group_name FROM " . DB_PREFIX . "product_attribute pa
	LEFT JOIN " . DB_PREFIX . "attribute a ON (pa.attribute_id = a.attribute_id)
	LEFT JOIN " . DB_PREFIX . "attribute_description ad ON (a.attribute_id = ad.attribute_id)
	LEFT JOIN " . DB_PREFIX . "attribute_group ag ON (a.attribute_group_id = ag.attribute_group_id)
	LEFT JOIN " . DB_PREFIX . "attribute_group_description agd ON (ag.attribute_group_id = agd.attribute_group_id)
	WHERE pa.product_id = " . (int)$product_id . "
		AND ad.language_id = agd.language_id
		AND pa.language_id = agd.language_id
		AND agd.language_id = " . (int)$this->config->get('config_language_id') . "
	ORDER BY ag.sort_order, agd.name, a.sort_order, ad.name");

	$product_attribute_data = array();

	foreach ($product_attribute_group_query->rows as $product_attribute) {
		$product_attribute_data[$product_attribute['attribute_id']][] = array(
			'attribute_id' => $product_attribute['attribute_id'],
			'name'         => $product_attribute['attribute_name'],
			'text'         => $product_attribute['text']
		);

		$product_attribute_group_data[$product_attribute['attribute_group_id']] = array(
			'attribute_group_id' => $product_attribute['attribute_group_id'],
			'name'               => $product_attribute['attribute_group_name'],
		);

		$product_attribute_group_data[$product_attribute['attribute_group_id']]['attribute'] =& $product_attribute_data[$product_attribute['attribute_id']];

	}

	return $product_attribute_group_data;
}

public function getProductOptions($product_id) {
	$product_option_data = array();
    $product_option_query = $this->db->query("SELECT pov.product_option_value_id, pov.option_value_id, ovd.name as product_option_value_name, od.name as option_name, ov.image, pov.quantity, pov.subtract, pov.price, pov.price_prefix, pov.points, pov.points_prefix, pov.weight, pov.weight_prefix, pov.product_option_id, pov.option_id, o.type, po.required
        FROM " . DB_PREFIX . "product_option_value pov
        LEFT JOIN " . DB_PREFIX . "option_value ov ON (pov.option_value_id = ov.option_value_id)
        LEFT JOIN " . DB_PREFIX . "option_value_description ovd ON (ov.option_value_id = ovd.option_value_id)
        LEFT JOIN " . DB_PREFIX . "product_option po ON (pov.product_option_id = po.product_option_id)
        LEFT JOIN `" . DB_PREFIX . "option` o ON (po.option_id = o.option_id)
        LEFT JOIN " . DB_PREFIX . "option_description od ON (o.option_id = od.option_id)
        WHERE po.product_id = " . (int)$product_id . "
            AND od.language_id = " . (int)$this->config->get('config_language_id') . "
        ORDER BY o.sort_order, ov.sort_order");

    $product_option_value_data = array();

    foreach ($product_option_query->rows as $product_option) {

    	if (in_array($product_option['type'], array('select','radio','checkbox','image'))) {

            $product_option_value_data[$product_option['product_option_id']][] = array(
                'product_option_value_id' => $product_option['product_option_value_id'],
                'option_value_id'         => $product_option['option_value_id'],
                'name'                    => $product_option['product_option_value_name'],
                'image'                   => $product_option['image'],
                'quantity'                => $product_option['quantity'],
                'subtract'                => $product_option['subtract'],
                'price'                   => $product_option['price'],
                'price_prefix'            => $product_option['price_prefix'],
                'points'                  => $product_option['points'],
                'points_prefix'           => $product_option['points_prefix'],
                'weight'                  => $product_option['weight'],
                'weight_prefix'           => $product_option['weight_prefix']
            );

            $product_option_data[$product_option['option_id']] = array(
				'product_option_id'    => $product_option['product_option_id'],
				'option_id'            => $product_option['option_id'],
				'name'                 => $product_option['option_name'],
				'type'                 => $product_option['type'],
				'required'             => $product_option['required']
			);

			$product_option_data[$product_option['option_id']]['option_value'] =& $product_option_value_data[$product_option['product_option_id']];

    	}else{
    		$product_option_data[$product_option['option_id']] = array(
				'product_option_id' => $product_option['product_option_id'],
				'option_id'         => $product_option['option_id'],
				'name'              => $product_option['option_name'],
				'type'              => $product_option['type'],
				'option_value'      => $product_option['option_value'],
				'required'          => $product_option['required']
			);
    	}
    }

	return $product_option_data;
}

 

По аналогии поступил с админкой (только опции) и с модулем CSV Product Export. Будьте внимательны при использовании этого кода, он изменен, а значит и vqmod модификации, которые работают с этим участком кода могут работать не так как ожидается. 

Змінено користувачем halfhope
  • +1 8
Надіслати
Поділитися на інших сайтах

Спасибо. Как раз в тему, попробую сейчас на реальном магазине с 20К товарами

Надіслати
Поділитися на інших сайтах

UPD: Накосячил слегка с аттрибутами:


public function getProductAttributes($product_id) {
	$product_attribute_group_data = array();

	$product_attribute_group_query = $this->db->query("SELECT a.attribute_id, ad.name as attribute_name, pa.text, ag.attribute_group_id, agd.name as attribute_group_name FROM " . DB_PREFIX . "product_attribute pa
	LEFT JOIN " . DB_PREFIX . "attribute a ON (pa.attribute_id = a.attribute_id)
	LEFT JOIN " . DB_PREFIX . "attribute_description ad ON (a.attribute_id = ad.attribute_id)
	LEFT JOIN " . DB_PREFIX . "attribute_group ag ON (a.attribute_group_id = ag.attribute_group_id)
	LEFT JOIN " . DB_PREFIX . "attribute_group_description agd ON (ag.attribute_group_id = agd.attribute_group_id)
	WHERE pa.product_id = " . (int)$product_id . "
		AND ad.language_id = agd.language_id
		AND pa.language_id = agd.language_id
		AND agd.language_id = " . (int)$this->config->get('config_language_id') . "
	ORDER BY ag.sort_order, agd.name, a.sort_order, ad.name");

	$product_attribute_data = array();

	foreach ($product_attribute_group_query->rows as $product_attribute) {
		$product_attribute_data[$product_attribute['attribute_group_id']][$product_attribute['attribute_id']] = array(
			'attribute_id' => $product_attribute['attribute_id'],
			'name'         => $product_attribute['attribute_name'],
			'text'         => $product_attribute['text']
		);

		$product_attribute_group_data[$product_attribute['attribute_group_id']] = array(
			'attribute_group_id' => $product_attribute['attribute_group_id'],
			'name'               => $product_attribute['attribute_group_name'],
		);

		$product_attribute_group_data[$product_attribute['attribute_group_id']]['attribute'] =& $product_attribute_data[$product_attribute['attribute_group_id']];

	}

	return $product_attribute_group_data;
}
Змінено користувачем halfhope
  • +1 2
Надіслати
Поділитися на інших сайтах

  • 1 year later...

У меня лог медленных запросов все также жалуется на этот запрос.

Товаров 1500 штук.

Таблица с атрибутами 28000 шт.

М.б. где индексы расставить дополнительные?

 

id     select_type     table     partitions     type     possible_keys     key     key_len     ref     rows     filtered     Extra     
1     SIMPLE     agd     NULL    ALL     PRIMARY     NULL    NULL    NULL    20     10.00     Using where; Using temporary; Using filesort
1     SIMPLE     ag     NULL    eq_ref     PRIMARY     PRIMARY     4     shop.agd.attribute_group_id     1     100.00     NULL
1     SIMPLE     a     NULL    ref     PRIMARY,attribute_group_id     attribute_group_id     4     shop.agd.attribute_group_id     3     100.00     Using where
1     SIMPLE     ad     NULL    eq_ref     PRIMARY,index2attr_lang     PRIMARY     8     shop.a.attribute_id,const     1     100.00     NULL
1     SIMPLE     pa     NULL    eq_ref     PRIMARY,attribute_id,language_id     PRIMARY     12     const,shop.a.attribute_id,const     1     100.00     NULL

 

 

Структура таблицы:

Table Create Table
attribute CREATE TABLE `attribute` (
 `attribute_id` int(11) NOT NULL AUTO_INCREMENT,
 `attribute_group_id` int(11) NOT NULL,
 `sort_order` int(3) NOT NULL,
 PRIMARY KEY (`attribute_id`),
 KEY `attribute_group_id` (`attribute_group_id`),
 KEY `sort_order` (`sort_order`)
) ENGINE=MyISAM AUTO_INCREMENT=39 DEFAULT CHARSET=utf8
attribute_description CREATE TABLE `attribute_description` (
 `attribute_id` int(11) NOT NULL,
 `language_id` int(11) NOT NULL,
 `name` varchar(64) NOT NULL,
 PRIMARY KEY (`attribute_id`,`language_id`),
 KEY `name` (`name`),
 KEY `index2attr_lang` (`language_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
attribute_group CREATE TABLE `attribute_group` (
 `attribute_group_id` int(11) NOT NULL AUTO_INCREMENT,
 `sort_order` int(3) NOT NULL,
 PRIMARY KEY (`attribute_group_id`),
 KEY `sort_order` (`sort_order`)
) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=utf8
attribute_group_description CREATE TABLE `attribute_group_description` (
 `attribute_group_id` int(11) NOT NULL,
 `language_id` int(11) NOT NULL,
 `name` varchar(64) NOT NULL,
 PRIMARY KEY (`attribute_group_id`,`language_id`),
 KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
product_attribute CREATE TABLE `product_attribute` (
 `product_id` int(11) NOT NULL,
 `attribute_id` int(11) NOT NULL,
 `language_id` int(11) NOT NULL,
 `text` text NOT NULL,
 PRIMARY KEY (`product_id`,`attribute_id`,`language_id`),
 KEY `attribute_id` (`attribute_id`),
 KEY `language_id` (`language_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

 

 

 

Надіслати
Поділитися на інших сайтах


Створіть аккаунт або увійдіть для коментування

Ви повинні бути користувачем, щоб залишити коментар

Створити обліковий запис

Зареєструйтеся для отримання облікового запису. Це просто!

Зареєструвати аккаунт

Вхід

Уже зареєстровані? Увійдіть тут.

Вхід зараз

×
×
  • Створити...

Important Information

На нашому сайті використовуються файли cookie і відбувається обробка деяких персональних даних користувачів, щоб поліпшити користувальницький інтерфейс. Щоб дізнатися для чого і які персональні дані ми обробляємо перейдіть за посиланням . Якщо Ви натиснете «Я даю згоду», це означає, що Ви розумієте і приймаєте всі умови, зазначені в цьому Повідомленні про конфіденційність.