Содержание
Оптимизация 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,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 тормозит за счет неправильной реализации получения списка товаров, качество этого кода оставляет желать лучшего. Ускорение в работе с базой данных ничего не принесет.
Три варианта:
- использовать кеширование Joomla на уровне страницы
- исправить этот ужасный код через кеширование (требуется доработка)
Оптимизация 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 }