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

Оптимизация движка под 120 000 товаров


Recommended Posts

1. nginx не везде, более того он вообще встречается в малых количествах на шаред хостингах, если не шаред хостинг и есть возможность рулить ПО на нём, то вообще к чему вопрос про кэшировние если всё лечится путём вливания денег в железо, смены СУБД/ПО, кластеризации и тд.

Более того по умолчанию он ничего не кеширует :) а вот потребление памяти если апачь сменить на него или сделать nginx фронтом, а апачь бекендом он снижает оооооооооочень хорошо :)

Сама операционка неплохо справляется с кешированием если уж на то пошло... но опять тут все больше зависит от количества памяти на серваке...

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


Более того по умолчанию он ничего не кеширует :) а вот потребление памяти если апачь сменить на него или сделать nginx фронтом, а апачь бекендом он снижает оооооооооочень хорошо :)

Сама операционка неплохо справляется с кешированием если уж на то пошло... но опять тут все больше зависит от количества памяти на серваке...

А лучше вообще апач выкинуть, оставить только nginx.

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

Я же написал что можно сменить, но тогда куча вопросов у юзеров возникает по поводу а как быть без .htaccess :)

в nginx всё реализуемо то что есть в .htaccess и даже больше.
Надіслати
Поділитися на інших сайтах

Итак, снова перемерял с изменениями из статьи toporchillo, получилось намного интереснее:

До изменений
Cache off: 7.22501 s 7.95 mb logs (2) sql (114) files (95) request (95)
Cache on: 3.48739 s 7.95 mb logs (2) sql (73) files (95) request (95)

После изменений
Cache off: 3.62278 s 7.98 mb logs (2) sql (103) files (95) request (95)
Cache on: 1.65011 s 7.98 mb logs (2) sql (62) files (95) request (95)

То есть выигрыш по скорости в 2 раза, что очень круто!

И я так думаю, что было бы еще лучше, если бы у меня была не 1.5.2.1, а 1.5.5.1, потому что есть пара моментов с несовместимостью. Буду благодарен, если кто-то укажет на причины ошибок:

1) Notice: Error: Unknown column 'p.rating' in 'field list'
Возникает из-за того, что в самом первом оптимизированном запросе

$query = $this->db->query("SELECT DISTINCT *, p.rating, pd.name AS name, p.image, m.name AS manufacturer,
(SELECT points FROM " . DB_PREFIX . "product_reward pr WHERE pr.product_id = p.product_id AND customer_group_id = '" . (int)$customer_group_id . "') AS reward,
(SELECT ss.name FROM " . DB_PREFIX . "stock_status ss WHERE ss.stock_status_id = p.stock_status_id AND ss.language_id = '" . (int)$this->config->get('config_language_id') . "') AS stock_status,
(SELECT wcd.unit FROM " . DB_PREFIX . "weight_class_description wcd WHERE p.weight_class_id = wcd.weight_class_id AND wcd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS weight_class,
(SELECT lcd.unit FROM " . DB_PREFIX . "length_class_description lcd WHERE p.length_class_id = lcd.length_class_id AND lcd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS length_class,
p.rating, (SELECT COUNT(*) AS total FROM " . DB_PREFIX . "review r2 WHERE r2.product_id = p.product_id AND r2.status = '1' GROUP BY r2.product_id) AS reviews,
p.sort_order
FROM " . DB_PREFIX . "product p
LEFT JOIN " . DB_PREFIX . "manufacturer m ON (p.manufacturer_id = m.manufacturer_id)
LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id)
WHERE p.product_id IN ('" . implode("','", array_keys($product_data)) . "')");
нет столбца rating (кмк, проблема в 1.5.2.1), как я это обошел - заменил в запросе в 2-х местах p.rating на
(SELECT AVG(rating) AS total FROM " . DB_PREFIX . "review r1 WHERE r1.product_id = p.product_id AND r1.status = '1' GROUP BY r1.product_id) AS rating
в итоге длина запроса увеличилась, но рейтинг стал учитываться (если можно проще это сделать, укажите плиз).

2) Undefined index: discount in Z:\home\localhost\ocstore\catalog\model\catalog\product.php
в 51-й строке:
$row['price'] = ($row['discount'] ? $row['discount'] : $row['price']);
я это просто закомментил, так как в магазине discounts вообще не используются.

3) И внезапно Notice: Undefined index: special in Z:\home\localhost\mstore\catalog\controller\product\category.php
в 196-й и 203-й строках
if ((float)$result['special']) {
$special = $this->currency->format($this->tax->calculate($result['special'], $result['tax_class_id'], $this->config->get('config_tax')));
} else {
$special = false;
}
if ($this->config->get('config_tax')) {
$tax = $this->currency->format((float)$result['special'] ? $result['special'] : $result['price']);
} else {
$tax = false;
}
Здесь я вообще не понял что случилось и ничего не смог придумать, потому что акционная цена special все равно показывается
Надіслати
Поділитися на інших сайтах

Обратите внимание на метод getProductList из моего commit'а

...

В этом запросе не нужны discount, special, rating - все это уже есть в $products

Либо я вас не понял, либо вы меня. На ваш коммит я и ориентировался и в точности все изменения перенес к себе в движок, но беда в том, как мне кажется, что у вас там версия 1.5.5.1, а у меня 1.5.2.1.

PS Нашел ошибку в запросе у себя, сказывается общая раскуроченность движка :-) Вроде теперь все окей работает

Змінено користувачем RGB
Надіслати
Поділитися на інших сайтах

Обратите внимание на метод getProductList из моего commit'а

private function getProductList(&$products) {
//...
$query = $this->db->query("SELECT DISTINCT *, p.rating, pd.name AS name, p.image, m.name AS manufacturer,
  (SELECT points FROM " . DB_PREFIX . "product_reward pr WHERE pr.product_id = p.product_id AND customer_group_id = '" . (int)$customer_group_id . "') AS reward,
  (SELECT ss.name FROM " . DB_PREFIX . "stock_status ss WHERE ss.stock_status_id = p.stock_status_id AND ss.language_id = '" . (int)$this->config->get('config_language_id') . "') AS stock_status,
  (SELECT wcd.unit FROM " . DB_PREFIX . "weight_class_description wcd WHERE p.weight_class_id = wcd.weight_class_id AND wcd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS weight_class,
  (SELECT lcd.unit FROM " . DB_PREFIX . "length_class_description lcd WHERE p.length_class_id = lcd.length_class_id AND lcd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS length_class,
  p.rating, (SELECT COUNT(*) AS total FROM " . DB_PREFIX . "review r2 WHERE r2.product_id = p.product_id AND r2.status = '1' GROUP BY r2.product_id) AS reviews,
  p.sort_order
FROM " . DB_PREFIX . "product p
LEFT JOIN " . DB_PREFIX . "manufacturer m ON (p.manufacturer_id = m.manufacturer_id)
LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id)
WHERE p.product_id IN ('" . implode("','", array_keys($product_data)) . "')");

$ret = array();
foreach ($query->rows as $row) {
  $row = array_merge($row, $product_data[$row['product_id']]); //!!!!!!!!
  $row['price'] = ($row['discount'] ? $row['discount'] : $row['price']);
  $row['rating'] = round($row['rating']);
  $row['reviews'] = ($row['reviews'] ? $row['reviews'] : 0);
  $ret[$row['product_id']] = $row;
}
return $ret;
 }

В этом запросе не нужны discount, special, rating - все это уже есть в $products

Почему вложенные SELECT'ы не преведены к JOIN? MySQL не умеет оптимизировать вложенные запросы и они работают медленее конструкций JOIN, это вроде как правило, а тут явное пренебрежение...

конструкция вида SELECT * это тоже не допустимо...

Змінено користувачем costas
Надіслати
Поділитися на інших сайтах

В таком случае у Даниэля полное пренебрежение MySQL ну или бенчмарк такой :-)

Перепишите без вложенных запросов, это будет предметнее.

А звездочку я и правда проглядел, и p.rating тоже не нужен в этом запросе. Помогли ошибку найти. Спасибо.

оригинальный код opencart это туши свет, об этом уже было сказано и обсуждено многократно, там вообще не пахнет разработчиками хоть както знакомыми с MySQL или же намеренно написанно с таким пренебрежением (что кстати более вероятно, на сапорте ведь они зарабатывают)...
Надіслати
Поділитися на інших сайтах

оригинальный код opencart это туши свет, об этом уже было сказано и обсуждено многократно, там вообще не пахнет разработчиками хоть както знакомыми с MySQL или же намеренно написанно с таким пренебрежением (что кстати более вероятно, на сапорте ведь они зарабатывают)...

Кстати, почему народ забывает про такую вещь, как поддержка опенкартом разных типов СУБД. В папке system\database\ кроме "драйвера" для MySql, валяются ещё для MSSql и PostgreSQL. Может быть из-за необходимости этой совместимости нет затачивания под одну определённую СУБД, в нашем случае MySql?

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

Кстати, почему народ забывает про такую вещь, как поддержка опенкартом разных типов СУБД. В папке system\database\ кроме "драйвера" для MySql, валяются ещё для MSSql и PostgreSQL. Может быть из-за необходимости этой совместимости нет затачивания под одну определённую СУБД, в нашем случае MySql?

И да, дествительно, отсюда и всё остальное...
Надіслати
Поділитися на інших сайтах

оригинальный код opencart это туши свет, об этом уже было сказано и обсуждено многократно, там вообще не пахнет разработчиками хоть както знакомыми с MySQL или же намеренно написанно с таким пренебрежением (что кстати более вероятно, на сапорте ведь они зарабатывают)...

Судя по качеству кода на сапорте они зарабатывают слабо. Чем больше разработчик зарабатывает на сапорте, тем больше думают о качестве разработки, тем больше вкладывают в разработку денег. Достаточно посмотреть на проект Magento (особенности его организации постарался описать здесь). Дефекты OC это, к сожалению, прямое следствие дефектов организации управления проектом в целом.

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


который, кстати, в двойном количестве.

убираем, получаем запрос

 $query = $this->db->query("SELECT DISTINCT *, pd.name AS name, p.image, m.name AS manufacturer,
  (SELECT points FROM " . DB_PREFIX . "product_reward pr WHERE pr.product_id = p.product_id AND customer_group_id = '" . (int)$customer_group_id . "') AS reward,
  (SELECT ss.name FROM " . DB_PREFIX . "stock_status ss WHERE ss.stock_status_id = p.stock_status_id AND ss.language_id = '" . (int)$this->config->get('config_language_id') . "') AS stock_status,
  (SELECT wcd.unit FROM " . DB_PREFIX . "weight_class_description wcd WHERE p.weight_class_id = wcd.weight_class_id AND wcd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS weight_class,
  (SELECT lcd.unit FROM " . DB_PREFIX . "length_class_description lcd WHERE p.length_class_id = lcd.length_class_id AND lcd.language_id = '" . (int)$this->config->get('config_language_id') . "') AS length_class,
  (SELECT COUNT(*) AS total FROM " . DB_PREFIX . "review r2 WHERE r2.product_id = p.product_id AND r2.status = '1' GROUP BY r2.product_id) AS reviews,
  p.sort_order
 FROM " . DB_PREFIX . "product p
 LEFT JOIN " . DB_PREFIX . "manufacturer m ON (p.manufacturer_id = m.manufacturer_id)
 LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id)
 WHERE p.product_id IN ('" . implode("','", array_keys($product_data)) . "')");

и рейтинг отображается, и время загрузки с кешем уменьшается до 1 с (а если отключить filterpro, то менше 0.5 с) - красота!

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

и рейтинг отображается, и время загрузки с кешем уменьшается до 1 с (а если отключить filterpro, то менше 0.5 с) - красота!

Я вот попробовал еще стандартный фильтр 1,5,5,1 включить http://demo5.demo.pl.ua/index.php?route=product/category&path=14_164

Работает шустро, но вот заполнять его в админке, это просто ппц, меня больше чем на 3 товара не хватило.

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

Я вот попробовал еще стандартный фильтр 1,5,5,1 включить

Работает шустро, но вот заполнять его в админке, это просто ппц, меня больше чем на 3 товара не хватило.

А может кто-нибудь подскажет, почему они решили идти по пути отдельных значений для фильтра, а не атрибутов/опций?

Меня это решение удивило. Может они где-то в дебрях форума обсуждали и можно почитать, почему так?

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


А может кто-нибудь подскажет, почему они решили идти по пути отдельных значений для фильтра, а не атрибутов/опций?

Меня это решение удивило. Может они где-то в дебрях форума обсуждали и можно почитать, почему так?

Наверное, разработчики брали пример с ebay и amazon. Там такие-же недофильтры ))

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

А может кто-нибудь подскажет, почему они решили идти по пути отдельных значений для фильтра, а не атрибутов/опций?

Меня это решение удивило. Может они где-то в дебрях форума обсуждали и можно почитать, почему так?

Удивило наверно потому что Вы или не торгуете или торгуете чем то что не имеет такого понятия как Длина от 100 до 200 или мощность от 1 квт до 2квт

и вот атрибуты у меня имеют порядка 100 промежуточных значений для этих опций фильтра.

на первом фильтре которым я пользовался еще на 0.1.9.4 использовались скажем так атрибуты для этого но там была настройка какой атрибут участвует в фильтрации а какой из них участвует в отображении в карточке товара ну и со сравнением история такая же

по поводу заполнил 3 товара и устал :-) то это действительно так и каждый должен заниматься своим делом, у меня для этого команда из 5ти человек сидит и кропотливо трудится в день выдают до 150 товаров с носа

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

по поводу заполнил 3 товара и устал :-) то это действительно так и каждый должен заниматься своим делом, у меня для этого команда из 5ти человек сидит и кропотливо трудится в день выдают до 150 товаров с носа

К сожалению, большинство заказчиков, этого не понимает. Зальют пачку товаров (с описанием в одну строку), и потом звонят мне: "почему фильтр не фильтрует?". :-D

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

К сожалению, большинство заказчиков, этого не понимает. Зальют пачку товаров (с описанием в одну строку), и потом звонят мне: "почему фильтр не фильтрует?". :-D

мне от таких больше нравится вопрос почему у меня в яндексе уже 3й месяц одна страница в индексе :-)

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

А кто-нибудь использовал такой инструмент для оптимизации скорости загрузки страниц, как асинхронные загрузчики? Я вот прикрутил к сайту yepnope, но результаты как-то не впечатляют абсолютно - со скриптами все окей, но при обновлении страницы долю секунды (видимо, пока загрузчик подхватит основную таблицу стилей (из кеша?) ) отображается голая страница без стилей, потом все становится ок, но что самое странное - такая картина при каждом обновлении страницы, будто бы браузер не кеширует цсс-ки, загруженные асинхронно - а в таком случае какой в них смысл вообще?

PS Забыл написать, такая картина только в хроме, фф и ие, опера и сафари не показывают страницу без стилей (или не успевают показать, или в них загрузчик работает быстрее)

PPS По времени получился выигрыш 0.2 с, хотя тут сложно точно измерять

Змінено користувачем RGB
Надіслати
Поділитися на інших сайтах

в nginx всё реализуемо то что есть в .htaccess и даже больше.

Да реализуемо кто спорит... можно дать юзеру возможность править конфиг в своей папки :)

Тока с просьбами сделать (переделать) рабочий сложный пример из реврайта в .htaccessе на nginx он всеравно к админам прибежит :)

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


парни, по моему совсем в дебри угнались). Большинство пользователей прозревают от матюков. На самом деле надо итогом выдать какой нить HOW TO, чтобы не было равнения на порномагнатов с вдсами на 4 к товаров. Ведь все можно запустить до вменяемых показателей малой кровью.

RGB,вы по моему перепробовали уже все методы, может сведете все воедино?

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


RGB попросите iostat у админов, ну тупо поглядите какой поток идет на запись и спросите что там за винты стоят у вас... а я вам скажу где главный косяк :)

Ах да и узнайте в noatime диски или нет :)

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


Нашел причины - реализация загрузчика. Насколько я понял, он работает быстрее (?) кеша браузера, а чтобы он ждал загрузки css, и уже после нее выполнял загрузку страницы (чтоб не мелькала голая страница без подключенных таблиц стилей), на гитхабе есть отдельная версия загрузчика. По цифрам получается достаточно интересно, хотя все зависит от того, насколько оптимально используются скрипты и таблицы стилей. У меня вот в хедере было прописано внешних 5 css и 10 js-файлов. Я загнал все это добро в загрузчик вот таким образом (jquery-1.7.1.min.js пришлось вынести до загрузчика):

<script type="text/javascript" src="catalog/view/javascript/jquery/jquery-1.7.1.min.js"></script>
<script src="catalog/view/javascript/yepnope.1.5.4-min.js"></script>
<script type="text/javascript">
 yepnope(['/catalog/view/theme/default/stylesheet/stylesheet.css', '/catalog/view/javascript/jquery/ui/themes/ui-lightness/jquery-ui-1.8.16.custom.css', '/catalog/view/javascript/jquery/colorbox/colorbox.css', '/catalog/view/theme/default/stylesheet/carousel.css', '/catalog/view/theme/default/stylesheet/livesearch.css', <?php foreach ($styles as $style) {echo "'/".$style['href']."', "; } ?>'/catalog/view/javascript/jquery/jquery.tooltip.js', '/catalog/view/javascript/jquery/ui/jquery-ui-1.8.16.custom.min.js', '/catalog/view/javascript/jquery/ui/external/jquery.cookie.js', '/catalog/view/javascript/jquery/colorbox/jquery.colorbox-min.js', '/catalog/view/javascript/jquery/tabs.js', '/catalog/view/javascript/jquery/jquery.jcarousel.min.js', '/catalog/view/javascript/common.js', '/catalog/view/javascript/livesearch.js', <?php foreach ($scripts as $script) {echo "'/".$script."', "; } ?> '/catalog/view/theme/default/extra/extra.js' ]);
</script>

В итоге получились такие цифры (отключил все модули, виджеты, метрики и т.п., мерял хромом через встроенный анализ по Timeline):

Без асинхронного загрузчика

Cache off: onload: 3.54 s, DOMContentLoaded: 3.40 s

Cache on: onload: 1.08 s, DOMContentLoaded: 976 ms

С асинхронным загрузчиком

Cache off: onload: 3.27 s, DOMContentLoaded: 3.13 s

Cache on: onload: 934 ms, DOMContentLoaded: 752 ms

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

RGB,вы по моему перепробовали уже все методы, может сведете все воедино?

Хорошая идея, надеюсь toporchillo исправит запросы, которые указали выше и выложит на гитхабе, а я всегда готов снова замерять эффективность и написать сюда уже в полном виде, что и как получилось оптимизировать.

RGB попросите iostat у админов, ну тупо поглядите какой поток идет на запись и спросите что там за винты стоят у вас... а я вам скажу где главный косяк :)

Ах да и узнайте в noatime диски или нет :)

Я все тесты сначала делаю локально, чтобы увидеть есть ли смысл вообще переносить изменения в реальный магазин. Сегодня ночью попробую все эти изменения с начала темы проверить на самом магазине, пока посетителей будет мало чтобы ничего не сломать, как я умею :)

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

Удивило наверно потому что Вы или не торгуете или торгуете чем то что не имеет такого понятия как Длина от 100 до 200 или мощность от 1 квт до 2квт

и вот атрибуты у меня имеют порядка 100 промежуточных значений для этих опций фильтра.

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

Теоретически я понимаю, плюс всего один - возможность отделения мух от котлет, если кол-во атрибутов и кол-во элементов, участвующих в фильтрации, слишком разное. Но на практике не встречал таких ситуаций. То ли атрибутов у каждого товара несколько десятков, а фильтр нужен не по всем, а только по парочке? Для снижения нагрузки на фильтр, получается? То ли ещё что?

Можете на конкретных примерах пояснить практические плюсы? Или только тот, что я назвал, и он все неудобства покрывает?

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


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

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

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

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

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

Вхід

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

Вхід зараз
×
×
  • Створити...

Important Information

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