Тормозит админка при большом количестве категорий

  • 7 Ответов
  • 495 Просмотров

0 Пользователей и 1 Гость просматривают эту тему.

Попал в довольно гнилую ситуацию: создал в магазине более 3 тысяч категорий, начала феерически тормозить админка на редактировании категорий и товаров.
Беглый анализ дал понять, что затык наступает в функции getTreeAllCategories из модели categories.
В принципе, не мудрено, в функции есть фрагмент:
Код
                    foreach ($categories as $key => $category){
                        ...
                        while ($category_parent_id || $i < 1000) {
                            foreach ($originalCategories as $originalKey => $originalCategory){
                                ...
                            }
                            $i++;
                        }
Три вложенных цикла, два из них размерностью порядка тысяч, да еще, как я понимаю, глубина вложенности категорий, в общем, несколько десятков миллионов итераций набирается.
Кто как с этим борется? Можно ли улучшить работу магазина в админке, не исправляя авторского кода? Мне в голову ничего пока не пришло - в этой функции ни одного плагина нет.

UPD
Функция buildTreeCategory работает еще ужаснее :( пришлось пока забить два грубых костыля.
« Последнее редактирование: 30.09.2015, 02:37:26 от novikov82 »

*

dmitry_stas

  • ********
  • 9626
  • 929
Беглый анализ дал понять, что затык наступает в функции getTreeAllCategories из модели categories.
указанный кусок кода выполняется только если isset($filter['text_search']) && !empty($filter['text_search']. соответственно, там далеко не несколько десятков миллионов проходов.

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

Функция buildTreeCategory работает еще ужаснее :( пришлось пока забить два грубых костыля.
есть тема Что нужно доделать в ЖШ. разработчики ее читают и если просьбы/советы действительно стоящие - то они включают это в апдейты. если вы считаете, что вам есть что предложить в плане оптимизации - предлагайте. уверен, что если будет что слушать, то вы будете услышаны :)
Тут дарят бакс просто за регистрацию! Успей получить!
Все советы на форуме раздаю бесплатно, то есть даром. Индивидуально бесплатно консультирую только по вопросам стоимости индивидуальных консультаций

указанный кусок кода выполняется только если isset($filter['text_search']) && !empty($filter['text_search']. соответственно, там далеко не несколько десятков миллионов проходов.
Да, вы правы, дело не в этом кусочке, а в функции recurseTree, она вызывается в другом месте и используется для построения дерева категорий.
Цитировать
приходилось иметь дело с магазинами, у которых было похожее количество категорий. не видел особых проблем. но вообще конечно согласен с вами, что там есть что оптимизировать.
Думаю, дело не столько в количестве категорий, а в большом уровне вложенности (так уж у меня получилось, что магазин сильно ветвится). Для рекурсии это, очевидно, важно.

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

*

dmitry_stas

  • ********
  • 9626
  • 929
сделайте ради интереса
Код
echo microtime(true);
до и после вызова recurseTree(), чтобы посмотреть сколько функция выполняется по времени.
думается мне что проблема все таки не в ней...
Тут дарят бакс просто за регистрацию! Успей получить!
Все советы на форуме раздаю бесплатно, то есть даром. Индивидуально бесплатно консультирую только по вопросам стоимости индивидуальных консультаций

сделайте ради интереса
Код
echo microtime(true);
до и после вызова recurseTree(), чтобы посмотреть сколько функция выполняется по времени.
думается мне что проблема все таки не в ней...
Сделал. Дело в ней :(
Ради эксперимента вставил такую штуку:
Код
function recurseTree($cat, $level, $all_cats, &$categories, $is_select) {
    $probil = '';
    if($is_select) {
        for ($i = 0; $i < $level; $i++) {
            $probil .= '-- ';
        }
        $cat->name = ($probil . $cat->name);
        $categories[] = JHTML::_('select.option', $cat->category_id, $cat->name,'category_id','name' );
    } else {
        $cat->level = $level;
        $categories[] = $cat;
    }
    foreach ($all_cats as $categ) {
   
        echo "x"; // <--- добавил для проверки

        if($categ->category_parent_id == $cat->category_id) {
            recurseTree($categ, ++$level, $all_cats, $categories, $is_select);
            $level--;
        }
    }
    return $categories;
}
Получил на выходе 7 миллионов иксов. Мне кажется, многовато.

*

dmitry_stas

  • ********
  • 9626
  • 929
какое время выполнения получили?
Тут дарят бакс просто за регистрацию! Успей получить!
Все советы на форуме раздаю бесплатно, то есть даром. Индивидуально бесплатно консультирую только по вопросам стоимости индивидуальных консультаций

какое время выполнения получили?
Пять секунд на локальной машине, на сервере не проверял, но, наверное, чуть меньше.
Но вообще диалог с вами подтолкнул на мысль. Функция recursiveTree написана, по сути, довольно бездарно. В рекурсии стоит цикл по всем категориям, в котором ищутся дочерние категории по идентификатора родителя. И при том количестве категорий, которое обычно бывает в магазинах, это не заметно. А я вот попался...
В общем, поправил ее малой кровью с использованием ассоциативного массива:
Код
function recurseTree($cat, $level, $all_cats, &$categories, $is_select) {
   static $cat_by_parent = null;
   if(!$cat_by_parent)
   {
$cat_by_parent = array();
foreach ($all_cats as $categ)
{
  if(!$cat_by_parent[$categ->category_parent_id])
$cat_by_parent[$categ->category_parent_id] = array();
  $cat_by_parent[$categ->category_parent_id][] = $categ;
}
    }

    $probil = '';
    if($is_select) {
        for ($i = 0; $i < $level; $i++) {
            $probil .= '-- ';
        }
        $cat->name = ($probil . $cat->name);
        $categories[] = JHTML::_('select.option', $cat->category_id, $cat->name,'category_id','name' );
    } else {
        $cat->level = $level;
        $categories[] = $cat;
    }
if($cat_by_parent)
    if(count($cat_by_parent[$cat->category_id]))
{
foreach ($cat_by_parent[$cat->category_id] as $categ) {
recurseTree($categ, ++$level, $all_cats, $categories, $is_select, $cat_by_parent);
$level--;
}
}
    return $categories;
}

Буду писать в предложения улучшений... Спасибо :)

*

dmitry_stas

  • ********
  • 9626
  • 929
угу, норм решение. я бы тоже так писал - исходя из цикла по родительским. по хорошему конечно тут тоже нужны проверки вне циклов, но и так насколько я вижу количество проходов снизилось с n*n до n+n. итого у вас 6 тыс вместо 7 млн :)
Тут дарят бакс просто за регистрацию! Успей получить!
Все советы на форуме раздаю бесплатно, то есть даром. Индивидуально бесплатно консультирую только по вопросам стоимости индивидуальных консультаций