Оптимизация VirtueMart

Стояла задача оптимизации VirtueMart. Тормозила витрина товаров. Вначале подозрение падало на базу данных.

Профилирование запросов к базе данных

Профилирование перконой не выявило ничего криминального:

# Overall: 38 total, 16 unique, 0 QPS, 0x concurrency ____________________
# Attribute          total     min     max     avg     95%  stddev  median
# ============     ======= ======= ======= ======= ======= ======= =======
# Exec time          405ms    10ms    11ms    11ms    11ms   364us    10ms
# Lock time              0       0       0       0       0       0       0
# Rows sent              0       0       0       0       0       0       0
# Rows examine           0       0       0       0       0       0       0
# Query size        15.76k      39     738  424.82  719.66  255.28  487.09

Общий объем всех ответов базы данных - 15.76k, минимум 39 байт, максимум - 738, время выполнения - 405 миллисекунд, минимум - 10 мс, максимум - 11.

Данные получены без использования кеша запросов, в них не попали «быстрые» запросы, выполняемые менее 0,01 сек.

0,4 секунды на базу данных - это, конечно, много. Надо стремиться, чтобы на всю страницу тратилось не более 0,2 сек.

Сами запросы достаточно быстрые, в оптимизации особой необходимости нет. Они берут «количеством».

В ситуации, когда на открытие страницы требуется 3 секунды, тормоза с базой данных в 0,4 сек - не самое узкое место. С «прогревом» кеша запросов это время существенно уменьшится.

Остальные показатели в норме. Нет слишком «длинных» запросов.

Профилирование PHP VirtueMart инструментом XHprof

Выше - результаты профилирование VirtueMart 2.0.20b с помощью XHprof. Сделано профилирование страницы витрины товаров.

Время выполнения - 1,1 сек, с включенным кешем запросов, как на действующем сайте.

После выборки продуктов по условию getProductListing, в скрипте по каждому продукту идет подгрузка детальной информации getProductSingle.

Это занимает 50% времени (IWall), причем этот процесс запускается для каждого продукта - итого 126 раз (Calls).

Каждый вызов метода getProductSingle делает пять запросов к БД (по коду) => 630 запросов. На картинке 1737 - общее количество на всю страницу.

База данных с таким количеством запросов VirtueMart справляется легко - всего 10% общего времени (mysqli_query). Спасибо кешу!

Правильно должно быть сделано так: один раз получается вся информация по выбранным продуктам, потом она раскидывается. Так обходятся пятью запросами.

Оптимизация VirtueMart: итоги

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

Три варианта:

Оптимизация VirtueMart: доработки

От версии к версии может отличаться. Вот мой случай:

administrator/components/com_virtuemart/models/product.php
public function getProduct ($virtuemart_product_id = NULL, $front = TRUE, $withCalc = TRUE, $onlyPublished = TRUE, $quantity = 1,$customfields = TRUE,$virtuemart_shoppergroup_ids=0) {
  if(true && !empty($virtuemart_product_id) && $front && JFactory::getConfig()->get('caching') >= 1) {
 
    if (!class_exists('CurrencyDisplay')) {
      require(JPATH_VM_ADMINISTRATOR . DS . 'helpers' . DS . 'currencydisplay.php'); // else 503 error
    }
 
    if($virtuemart_shoppergroup_ids !=0 and is_array($virtuemart_shoppergroup_ids)){
      $virtuemart_shoppergroup_idsString = implode('',$virtuemart_shoppergroup_ids);
    } else {
      $virtuemart_shoppergroup_idsString = $virtuemart_shoppergroup_ids;
    }
    $withRating = $this->withRating?TRUE:0;
    $productKey = $virtuemart_product_id.'-'.$front.'-'.$withCalc.'-'.$onlyPublished.'-'.$quantity.'-'.$customfields.'-'.$virtuemart_shoppergroup_idsString.'-'.$withRating;
 
    $mycache  = JFactory::getCache('com_virtuemart_speedup', '');
    $mycache->setLifeTime(86400);
 
    $cache_content = $mycache->get($productKey);
 
    if (!is_object($cache_content)) {
      $cache_content = $this->_getProduct($virtuemart_product_id, $front, $withCalc, $onlyPublished, $quantity, $customfields, $virtuemart_shoppergroup_ids);
      $mycache->store($cache_content, $productKey);
    }
 
    return $cache_content;
  } else {
    return $this->_getProduct($virtuemart_product_id, $front, $withCalc, $onlyPublished, $quantity, $customfields, $virtuemart_shoppergroup_ids);
  }
 
}
 
public function _getProduct ($virtuemart_product_id = NULL, $front = TRUE, $withCalc = TRUE, $onlyPublished = TRUE, $quantity = 1,$customfields = TRUE,$virtuemart_shoppergroup_ids=0) {
  // здесь старый вариант функции _getProduct
}