Ускорение работы магазина (1.5.*)
CodeNull
Новичок
: 3
: 16-08-2015


16-08-2015 20:47
Всем доброго времени суток.

Столкнулся с проблемой очень низкой производительности магазина, даже на мощном железе.
Судя по различным профайлам (как встроенным в престу, так и xdebug) существует несколько причин замедления, но все они связаны в той или иной степени с количеством аттрибутов и комбинаций товаров. В базе магазина достаточно много товаров с большим количеством свойств.

Теперь сами проблемы:
1. После перехода с 1.5.2 на 1.5.6 резко снизилась скорость загрузки страниц некоторых категорий товаров. С 2-3 сек до 25-30сек. 90% этого времени занимает выполнение следующего запроса к базе данных из /classes/Category.php стр. ~615 (метод getProducts()):
Код:
SELECT
  p.*,
  product_shop.*,
  stock.out_of_stock,
  IFNULL(stock.quantity, 0) AS quantity,
  MAX(product_attribute_shop.id_product_attribute) id_product_attribute,
  product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity,
  pl.`description`,
  pl.`description_short`,
  pl.`available_now`,
  pl.`available_later`,
  pl.`link_rewrite`,
  pl.`meta_description`,
  pl.`meta_keywords`,
  pl.`meta_title`,
  pl.`name`,
  MAX(image_shop.`id_image`) id_image,
  il.`legend`,
  m.`name` AS manufacturer_name,
  cl.`name` AS category_default,
  DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(),
  INTERVAL 3
  DAY)) > 0 AS new,
  product_shop.price AS orderprice
FROM `mb_category_product` cp
  LEFT JOIN `mb_product` p
    ON p.`id_product` = cp.`id_product`
  INNER JOIN mb_product_shop product_shop
    ON (product_shop.id_product = p.id_product
    AND product_shop.id_shop = 1)
  LEFT JOIN `mb_product_attribute` pa
    ON (p.`id_product` = pa.`id_product`)
  LEFT JOIN mb_product_attribute_shop product_attribute_shop
    ON (product_attribute_shop.id_product_attribute = pa.id_product_attribute
    AND product_attribute_shop.id_shop = 1
    AND product_attribute_shop.`default_on` = 1)
  LEFT
  JOIN mb_stock_available stock
    ON (stock.id_product = p.id_product
    AND stock.id_product_attribute = IFNULL(`product_attribute_shop`.id_product_attribute, 0)
    AND stock.id_shop = 1)
  LEFT JOIN `mb_category_lang` cl
    ON (product_shop.`id_category_default` = cl.`id_category`
    AND cl.`id_lang` = 1
    AND cl.id_shop = 1)
  LEFT JOIN `mb_product_lang` pl
    ON (p.`id_product` = pl.`id_product`
    AND pl.`id_lang` = 1
    AND pl.id_shop = 1)
  LEFT JOIN `mb_image` i
    ON (i.`id_product` = p.`id_product`)
  LEFT JOIN mb_image_shop image_shop
    ON (image_shop.id_image = i.id_image
    AND image_shop.id_shop = 1
    AND image_shop.cover = 1)
  LEFT JOIN `mb_image_lang` il
    ON (image_shop.`id_image` = il.`id_image`
    AND il.`id_lang` = 1)
  LEFT JOIN `mb_manufacturer` m
    ON m.`id_manufacturer` = p.`id_manufacturer`
WHERE product_shop.`id_shop` = 1
AND cp.`id_category` = 18
AND product_shop.`active` = 1
AND product_shop.`visibility` IN ("both", "catalog")
GROUP BY product_shop.id_product
ORDER BY `orderprice` ASC
LIMIT 0, 20

Если убрать соединение с таблицей stock то время выполнения запроса с 27 секунд падает до 0.8 сек. В магазине не используется контроль остатков. Временно поставил заплатку в запрос, категории стали открываться приемлимое время.

Вся загвоздка в том, что в старой версии престы (1.5.6.2), этот запрос вообще не выполнялся о_О Возможно есть некая волшебная настройка, которая приводит к выполнению данного запроса?
 
  
Алекс


Администратор
: 4516
: Красноярск
: 29-05-2009


17-08-2015 16:30
Цитата:( CodeNull @ 16-08-2015 21:47 Смотреть сообщение )
Возможно есть некая волшебная настройка, которая приводит к выполнению данного запроса?

Нет.
Код из метода getProducts в 1.5.2:
Код:
'.Product::sqlStock('p', 'product_attribute_shop', false, $context->shop).'

Код из метода getProducts в 1.5.6.2:
Код:
'.Product::sqlStock('p', 'product_attribute_shop', false, $context->shop).'


Код выше одинаков.
Метод Product::sqlStock также не изменился.

Верный вариант:
Цитата:( CodeNull @ 16-08-2015 21:47 Смотреть сообщение )
Временно поставил заплатку в запрос, категории стали открываться приемлимое время.
 
  
CodeNull
Новичок
: 3
: 16-08-2015


18-08-2015 20:13
Код не менялся, вы правы. Поменялись условия его вызова. При открытии категории вначале выполняется assignProductList() из /controllers/front/CategoryController.php.

И если я правильно понял код (с php не работал больше 5 лет), то в старой версии у меня срабатывал хук actionProductListOverride, а в новой версии он не срабатывает и вызывается стандартный код с указанным запросом. По модулям изменений особых небыло, только отключил blocklayered из-за тормозов.

Код:
public function assignProductList()
    {
        $hookExecuted = false;
        Hook::exec('actionProductListOverride', array(
            'nbProducts' => &$this->nbProducts,
            'catProducts' => &$this->cat_products,
            'hookExecuted' => &$hookExecuted,
        ));

        // The hook was not executed, standard working
        if (!$hookExecuted)
        {
            $this->context->smarty->assign('categoryNameComplement', '');
            $this->nbProducts = $this->category->getProducts(null, null, null, $this->orderBy, $this->orderWay, true);
            $this->pagination((int)$this->nbProducts); // Pagination must be call after "getProducts"
            $this->cat_products = $this->category->getProducts($this->context->language->id, (int)$this->p, (int)$this->n, $this->orderBy, $this->orderWay);
        }
        // Hook executed, use the override
        else
            // Pagination must be call after "getProducts"
            $this->pagination($this->nbProducts);

        foreach ($this->cat_products as &$product)
        {
            if ($product['id_product_attribute'] && isset($product['product_attribute_minimal_quantity']))
                $product['minimal_quantity'] = $product['product_attribute_minimal_quantity'];
        }

        $this->context->smarty->assign('nb_products', $this->nbProducts);
    }
 
  
Алекс


Администратор
: 4516
: Красноярск
: 29-05-2009


18-08-2015 21:02
Цитата:( CodeNull @ 18-08-2015 21:13 Смотреть сообщение )
И если я правильно понял код (с php не работал больше 5 лет), то в старой версии у меня срабатывал хук actionProductListOverride, а в новой версии он не срабатывает и вызывается стандартный код с указанным запросом.

Бред.
Если установлен модуль blocklayered (или любой другой работающий с хуком actionProductListOverride / productListAssign) сбором и обработкой товаров занимается данный модуль иначе выполняется метод Category::getProducts.

Цитата:( CodeNull @ 18-08-2015 21:13 Смотреть сообщение )
отключил blocklayered из-за тормозов.

Скачайте последнюю версию модуля blocklayered.
 
  
CodeNull
Новичок
: 3
: 16-08-2015


19-08-2015 11:00
Цитата:( Алекс @ 19-08-2015 00:02 Смотреть сообщение )
Цитата:( CodeNull @ 18-08-2015 21:13 Смотреть сообщение )
отключил blocklayered из-за тормозов.
Скачайте последнюю версию модуля blocklayered.
Ставил последнию версию. Её тоже пришлось править, конкретно в методе getProductByFilters() есть запрос который цепляет остатки:
Код:
$this->products = Db::getInstance()->executeS('
                SELECT
                    p.*,
                    '.($alias_where == 'p' ? '' : 'product_shop.*,' ).'
                    '.$alias_where.'.id_category_default,
                    pl.*,
                    MAX(image_shop.`id_image`) id_image,
                    il.legend,
                    m.name manufacturer_name,
                    '.(Combination::isFeatureActive() ? 'MAX(product_attribute_shop.id_product_attribute) id_product_attribute,' : '').'
                    DATEDIFF('.$alias_where.'.`date_add`, DATE_SUB("'.date('Y-m-d').' 00:00:00", INTERVAL '.(int)$nb_day_new_product.' DAY)) > 0 AS new,
                    stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity'.(Combination::isFeatureActive() ? ', MAX(product_attribute_shop.minimal_quantity) AS product_attribute_minimal_quantity' : '').'
                FROM '._DB_PREFIX_.'cat_filter_restriction cp
                LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = cp.`id_product`
                '.Shop::addSqlAssociation('product', 'p').
                (Combination::isFeatureActive() ?
                'LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (p.`id_product` = pa.`id_product`)
                '.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop='.(int)$context->shop->id):'').'
                LEFT JOIN '._DB_PREFIX_.'product_lang pl ON (pl.id_product = p.id_product'.Shop::addSqlRestrictionOnLang('pl').' AND pl.id_lang = '.(int)$cookie->id_lang.')
                LEFT JOIN `'._DB_PREFIX_.'image` i  ON (i.`id_product` = p.`id_product`)'.
                Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').'
                LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$cookie->id_lang.')
                LEFT JOIN '._DB_PREFIX_.'manufacturer m ON (m.id_manufacturer = p.id_manufacturer)
                '.Product::sqlStock('p', 0).'
                WHERE '.$alias_where.'.`active` = 1 AND '.$alias_where.'.`visibility` IN ("both", "catalog")
                GROUP BY product_shop.id_product
                ORDER BY '.Tools::getProductsOrder('by', Tools::getValue('orderby'), true).' '.Tools::getProductsOrder('way', Tools::getValue('orderway')).' , cp.id_product'.
                ' LIMIT '.(((int)$this->page - 1) * $n.','.$n));


Из него так же пришлось вырезать соединение с остаткими (Stock), иначе mysql уходил в нирвану при открытии некоторых разделов каталога.

П.С.: возможно эти костыли помогут кому-то ещё.


Сообщение отредактировано CodeNull 19-08-2015 14:02 ...