Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Общие вопросы > Оптимизация сайта, прошу помощи


Автор: sanich_ 2.3.2008, 19:16
Вопрос в следующем

еще не законченная страница сайта генерируется за 0,52 ms на локальном сервере,
это время включает все операции (выборка из базы, работа php)

Хороший ли это показатель? Надо ли оптимизировать?


Страница состоит из двух  частей:

1 левая рубрикатор - на ее часть отводится примерно 0,25-0,30 ms
2 Центральная список данных - очень большая 0,25-0,3ms думаю здесь нормально

Вот кусок кода левой проблемной части, есть ли здесь проблемные места?

Что касается базы, то выбоки не дожны тормозить, построены индексы по полям:
order_
id_categ
 
Код

$ds=mysql_query("select id_categ,categ from categ order by order_");
  while ($ds_arr = mysql_fetch_array($ds))
  {
   echo ('<tr><td class="CategTDItem"><a href="#" onClick="expand_cat('.$ds_arr['id_categ'].');return false;">'.$ds_arr['categ'].'</a></td></tr>');
   $ds2=mysql_query("select * from rubrika where id_categ=".$ds_arr['id_categ']." order by order_");
   $count_rows=mysql_num_rows($ds2);
   if ($count_rows>0)
   {
     if ($ds_arr['id_categ']==$id_categ) $display='block'; else $display='none';
     echo ('<tr><td><div style = "position:relative; display:'.$display.';" id="'.$ds_arr['id_categ'].'" >');
     echo ('<table cellspacing = "0" cellpadding = "0" border="0" width="100%">');
     while ($ds2_arr = mysql_fetch_array($ds2))
     {
      if ($ds2_arr['id_rubrika']==$_GET['id_rubrika']) $font_style='style="color:#CB7B02;"'; else $font_style='';       
      echo ('<tr><td class="RubrikaTDItem"><a href="index.php?id_rubrika='.$ds2_arr['id_rubrika'].'" '.$font_style.'>'.$ds2_arr['rubrika'].'</a></td></tr>');
     }
     echo ("</table></div></td></tr>");
   }
  } 



Автор: GeneralElectric 2.3.2008, 20:52
скажи, ты буковки ms для красоты пишешь, или это действительно миллисекунды?

Цитата(sanich_ @  2.3.2008,  19:16 Найти цитируемый пост)
Что касается базы, то выбоки не дожны тормозить

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

Автор: Sannis 3.3.2008, 09:37
Цитата(sanich_ @  2.3.2008,  19:16 Найти цитируемый пост)
еще не законченная страница сайта генерируется за 0,52 ms на локальном сервере,это время включает все операции (выборка из базы, работа php)

Это первое открытие страницы, или второе? (результаты обычно отличаются из-за внутреннего кеширования в php)
Понимаю, что сравнивать нехорошо, но форум, подобный этому, со второго раза у меня отображается за 0.05-0.08 s. Так что есть к чему стремиться smile

Автор: sanich_ 3.3.2008, 12:06
1. Это действительно милисекунды
2. Странно как то но сейчас страница генерируется за 0.13ms после кеширования, когда проставил индексы этого срузу не ощутилось, индексы начинают работать после перезагрузки сервера?

Автор: skyboy 3.3.2008, 12:35
Цитата(sanich_ @  3.3.2008,  11:06 Найти цитируемый пост)
 индексы начинают работать

cсразу после создания, насколько мне известно. с другой стороны, индексы - только механизм. если твои запросы не допускают использование индексов, то наличие/отсутствин индекса никак не отобразится на скорости.

Автор: awers 3.3.2008, 13:18
жутко криво... очень много времени.

Автор: sanich_ 3.3.2008, 13:24
Цитата(skyboy @ 3.3.2008,  12:35)
Цитата(sanich_ @  3.3.2008,  11:06 Найти цитируемый пост)
 индексы начинают работать

cсразу после создания, насколько мне известно. с другой стороны, индексы - только механизм. если твои запросы не допускают использование индексов, то наличие/отсутствин индекса никак не отобразится на скорости.

Давайте попорядку..

1. Я построил индексы по полям которые учавствуют в запросе where и order, не силен в оптимизации но мои знания подсказали что нужно сделать именно так.

Результат: страница стала генерироваться за 0,13-0,10 ms после кешировая запросов

Это долго?Что можно еще сделать? Код PHP оптимизируемый?

Автор: awers 3.3.2008, 13:26
sanich_, обычно оптимизируют логику, а не просто код

Автор: GeneralElectric 3.3.2008, 13:50
sanich_, наверное, ты имеешь в виду все-таки секунды.
а оптимизировать работу БД надо не теоретически,  "я там где-то какие-то индексы поставил", а практически.
В частности, измерять время выполнения запросов. А не заявлять, что они работают быстро, хотя ты это никак не проверил

Автор: mishaSL 3.3.2008, 14:05
sanich_, дам свои несколько комментариев:
1. Код который ты привел очень "кривой". Построение этого списка нужно делать с помощью 1 запроса. Нужно избегать вложенные с цикл запросы.
2. На мой взгляд, если БД практически пустая и ты это делаешь на локальном сервере, то это большое время. (Опять таки если ты имеешь в виду 0.52 сек, если это 0.52 миллисекунды то  это очень хороший результат).
3. Измерей кол-во запросов к БД при генерации одной страницы, их кол-во желательно тоже минимизировать.

Автор: sanich_ 3.3.2008, 14:14
Цитата(mishaSL @ 3.3.2008,  14:05)
sanich_, дам свои несколько комментариев:
1. Код который ты привел очень "кривой". Построение этого списка нужно делать с помощью 1 запроса. Нужно избегать вложенные с цикл запросы.
2. На мой взгляд, если БД практически пустая и ты это делаешь на локальном сервере, то это большое время. (Опять таки если ты имеешь в виду 0.52 сек, если это 0.52 миллисекунды то  это очень хороший результат).
3. Измерей кол-во запросов к БД при генерации одной страницы, их кол-во желательно тоже минимизировать.

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

Автор: sanich_ 4.3.2008, 09:10
Код


$ds=mysql_query("select t.id, t.name_, t.type_ from ".
                                                "(select id_categ id, categ name_,0 type_, order_ categ_order, -1 rubrika_order from categ".
                                                " union all".
                                                " select r.id_rubrika id, r.rubrika,1 type_, c.order_, r.order_ from rubrika r,categ c where r.id_categ=c.id_categ) t ".
                  "order by t.categ_order, t.rubrika_order");
  while ($ds_arr = mysql_fetch_array($ds))
  {
   if ($ds_arr['type_']==0)
   {
    if ($is_first==2) echo '</table></td></tr>'; 
    $cur_id_categ=$ds_arr['id'];
    echo '<tr><td class="CategTDItem"><a href="#" onClick="expand_cat('.$ds_arr['id'].');return false;">'.$ds_arr['name_'].'</a></td></tr>';
    $is_first=1;
   }
   else
   {
    
    if ($is_first==1) 
    {
     if ($cur_id_categ==$id_categ) $display='block'; else $display='none';
     echo '<tr style = "position:relative; display:'.$display.';" id="'.$cur_id_categ.'"><td><table cellspacing = "0" cellpadding = "0" width="100%">';
    } 
    
    if ($ds_arr['id']==$_GET['id_rubrika']) $font_style='style="color:#CB7B02;"'; else $font_style='';       
    echo '<tr><td class="RubrikaTDItem"><a href="index.php?id_rubrika='.$ds_arr['id'].'" '.$font_style.'>'.$ds_arr['name_'].'</a></td></tr>';
    $is_first=2;
   }    


Переписал страницу, ушел от вложенных в цикл подзапросов, но проблема со скоростью не разрешилась, теперь сам запрос долго выполянется

Только на запрос уходит ~16ms-30ms
Код


select t.id, t.name_, t.type_ from
             (select id_categ id, categ name_,0 type_, order_ categ_order, -1 rubrika_order from categ
              union all
              select r.id_rubrika id, r.rubrika,1 type_, c.order_, r.order_ from rubrika r,categ c where r.id_categ=c.id_categ) t
order by t.categ_order, t.rubrika_order


План запроса показал следующее, подскажите пожалуйста как оптимизировать

Автор: GeneralElectric 4.3.2008, 11:20
mishaSL, вот хотел я возразить, но не стал.
И что получилось.
От такой вот "оптимизации" - один вред.
Во-первых, запросы в цикле - не всегда зло. 20, скажем, однострочных выборок  по первичному индексу никого ничем не затормозят.
Во-вторых, желание "хоть тушкой, хоть чучелком" но запихнуть все в один запрос приводит к появлению таких вот монстров. Злокачественных опухолей на теле реляционной модели. Суть осталась та же, но можем гордо рапортовать, что один запрос!!!
Вообще, появление в мускуле вложенных ползапросов и юниона принесло очень много вреда, учитывая тот факт, что в подавляющей массе своей люди, использующие мускуль, являются новичками.
Это как коробка-автомат. Да, научиться ездить очень легко. Но водителем он уже никогда не станет. 

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

sanich_, 
если вернуться к первому варианту.
Сколько времени выполняется запрос 
select id_categ,categ from categ order by order_
в консоли mysql.exe
и сколько - вложенный в него, из рубрик. один, не все.

они должны исполняться, каждый, не дольше одной сотой секунды (мускуль пишет что-то вроде (0.05 sec).
причем выполнять надо не один раз, а несколько раз подряд.

И постарайся не путаться с указанием времени. Это не так уж и сложно - отличать секунды от миллисекунд и писать десятичные доли секунды.
а ещё лучше - не от себя время пиши, а копируй из консоли.

Автор: mishaSL 4.3.2008, 11:31
sanich_, SQL запрос должен быть примерно такой:
Код

SELECT `categ`.`id_categ`, `categ`.`categ`, `rubrika`.`id_rubrika`, `rubrika`.`rubrika`
FROM `categ` LEFT JOIN `rubrika` ON `categ`.`id_categ` = `rubrika`.`id_categ`  
ORDER BY `categ`.`categ_order`, `rubrika`.`rubrika_order` ASC


Как его результаты обработать в PHP думаю уже разберешься.

Добавлено через 8 минут и 36 секунд
GeneralElectric, я не говорю что всегда стоит все пихать в один запрос, но конкретно в данной ситуации - это практически стандартный и очень простой запрос. Здесь не должно быть никаких UNION и тем более вложенных запросов. Именно по-этому я дал такую критику вложенных в цикл запросов, т.к. в данном случае не используется никаких LIMIT, что может с ростом сайта привести к проблемам в его загрузке.

Автор: sanich_ 4.3.2008, 12:17
to GeneralElectric
Ну если чесно то самый первый вариант получения данных был хотябы проще для понимания, выбрал категорию, выбрад рубрики и.д.
А щас большой массив данных который еще пришлось по хитрому обработать, но повышения производительности это не прибавило!

Консоли Mysql у меня нет, стоит пакет TopServer, в папке mysql5 только mysqld-max-nt.exe ядро и тулза для дампа
Короче, я пользуюсь SQL yog enterprise

Запрос
select id_categ,categ from categ order by order_
в статус баре пишет то 0ms то 16ms, но чаще 0ms при многократном вызове, всего строк 42
Возможно не точно время определяется, подскажите как его точно засечь

Запрос
select id_rubrika,rubrika from rubrika where id_categ=2
7 строк, время 0ms

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

Щас и не знаю как проверить, разве что на ПХП мастерить цикл.

Автор: GeneralElectric 4.3.2008, 12:20
черт возьми. 16 ms - это ОТЛИЧНАЯ СКОРОСТЬ!
О чем тебе уже писали.

Автор: awers 4.3.2008, 13:34
я только сейчас понял о чем говорил автор ... не ~0,50сек, а 0,050 ))) да супер... нормальная скорость

Автор: mishaSL 4.3.2008, 13:51
Возможно скорость каждого отдельного запроса хорошая, но 43 запроса для построения меню - это на мой взгляд перебор. 
Если у автора возникли проблемы с обработкой данных полученных после выполнения запроса с LEFT JOIN, то вот накидал примерно как это должно выглядеть на мой взгляд:
Код

<?php

$sql = "SELECT `categ`.`id_categ`, `categ`.`categ`, `rubrika`.`id_rubrika`, `rubrika`.`rubrika`
        FROM `categ` LEFT JOIN `rubrika` ON `categ`.`id_categ` = `rubrika`.`id_categ`
        ORDER BY `categ`.`categ_order`, `rubrika`.`rubrika_order` ASC";

$query = mysql_query($sql);
$arr = array();
while ($row = mysql_fetch_array($query, MYSQL_NUM)) {
    $row[0] = intval($row[0]);
    $row[2] = intval($row[2]);
    if (!isset($arr[$row[0]])) {
        $arr[$row[0]] = array('name'=>$row[1], 'rubriks'=>array());
    }
    if (!empty($row[2])) {
        $arr[$row[0]]['rubriks'][$row[2]]=$row[3];
    }
}
//получили массив $arr со всеми нужными нам данными, далее занимаемся выводом и т.д.
$id_categ   = $_GET['id_categ']; //это по всей видимости твоя переменная с текущей категорией
$id_rubrika = $_GET['id_rubrika']; // а это с текущей рубрикой
foreach($arr as $k=>$v) {
    ?>
    <tr><td class="CategTDItem"><a href="#" onClick="expand_cat(<?=$k?>);return false;"><?=$v['name']?></a></td></tr>
    <?
    if (!empty($v['rubriks'])) {
        ?>
        <tr><td><div style = "position:relative; display:<?=($k == $id_categ ? 'block' : 'none')?>;" id="<?=$k?>" >
        <table cellspacing = "0" cellpadding = "0" border="0" width="100%">
        <?
        foreach($v['rubriks'] as $kk=>$vv) {
            ?>
            <tr><td class="RubrikaTDItem"><a href="index.php?id_rubrika=<?=$kk?>" <?=($kk == $id_rubrika ? 'style="color:#CB7B02;"' : '')?>><?=$vv?></a></td></tr>
            <?
        }
        ?>
        </table></div></td></tr>
        <?
    }
}
unset($arr);
?>


Код не проверял, но он должен работать и логику из него можно понять.

Автор: sanich_ 5.3.2008, 14:53
mishaSL, спасибо за код. Но я не много по другому сделал. Думаю что ваш код не оптимален т.к. в нем 2 цикла, а можно обойтись и одним

У меня сейчас 42категори, 258 рубрик

Короче ваш код протестил вышло время 0,19-0,20 sec   я не знаю стоит ли пытаться еще оптимизировать или нет

Мой код вытянул на 0,14-0,15sec Небольшая разница, но все же

Код

<TABLE cellSpacing=0 cellPadding=0 width="200" class="TableBorder">
 <?
  if (isset($_GET['id_rubrika']))
  {
   $ds=mysql_query("select id_categ from rubrika where id_rubrika=".$_GET['id_rubrika']);
   $ds_arr = mysql_fetch_array($ds);
   $id_categ=$ds_arr['id_categ'];
  } 
  else $id_categ=0;
 ?>

 <script language="javascript"> var prev_id=<? echo $id_categ;?>; </script>

 <TR><TD class="CaptionTDList">рубрикатор</TD></TR>
 <?
  $ds=mysql_query("(select id_categ id,categ name_,order_ categ_order,-1  rubrika_order,' ' count_ from categ) union all
                   (select r.id_rubrika id, r.rubrika,c.order_, r.order_,' ' as count_ from rubrika r ,categ c
                    where r.id_categ=c.id_categ )
                   order by categ_order, rubrika_order");
  while ($ds_arr = mysql_fetch_array($ds))
  {
   if ($ds_arr['rubrika_order']==-1)
   {
    if ($is_first==2) echo '</table></td></tr>'; 
    $cur_id_categ=$ds_arr['id'];
    echo '<tr><td class="CategTDItem"><a href="#" onClick="expand_cat('.$ds_arr['id'].');return false;">'.$ds_arr['name_'].'</a></td></tr>';
    $is_first=1;
   }
   else
   {
    
    if ($is_first==1) 
    {
     if ($cur_id_categ==$id_categ) $display='block'; else $display='none';
     echo '<tr style = "position:relative; display:'.$display.';" id="'.$cur_id_categ.'"><td><table cellspacing = "0" cellpadding = "0" width="100%">';
    } 
    
    if ($ds_arr['id']==$_GET['id_rubrika']) $font_style='style="color:#CB7B02;"'; else $font_style='';       
    echo '<tr><td class="RubrikaTDItem"><a href="index.php?id_rubrika='.$ds_arr['id'].'" '.$font_style.'>'.$ds_arr['name_'].'</a></td></tr>';
    $is_first=2;
   }    
   
  } 
 ?>
</TABLE>

Автор: awers 5.3.2008, 14:59
sanich_, опиши метод замера скорости

Автор: sanich_ 5.3.2008, 15:26
Это скрипт позволяющий вычислять время работы запросов.

В странице точно так же - в начале страницы засекаем в конце, после обработки кода, выводим

Код

<?php
$hostname = "localhost";
$database = "dos6";
$username = "root";
$password = "";
$connect = mysql_pconnect($hostname, $username, $password) or trigger_error(mysql_error(),E_USER_ERROR); 
mysql_select_db($database, $connect);

$start_time = microtime(); 
$start_array = explode(" ",$start_time); 
$start_time = $start_array[1] + $start_array[0]; 


for ($i=1;$i<=100;$i++)
{
$ds=mysql_query("(select id_categ id,categ name_,order_ categ_order,-1  rubrika_order,' ' count_ from categ) union all
                   (select r.id_rubrika id, r.rubrika,c.order_, r.order_,(select count(1) from object where blok<>2 and id_rubrika=r.id_rubrika) as count_ from rubrika r ,categ c
                    where r.id_categ=c.id_categ )
                   order by categ_order, rubrika_order");*/            
$ds=mysql_query("select r.id_rubrika id, r.rubrika,c.order_, r.order_,(select count(1) from object where blok<>2 and id_rubrika=r.id_rubrika) as count_ from rubrika r ,categ c
where r.id_categ=c.id_categ");                                
}


$end_time = microtime(); 
$end_array = explode(" ",$end_time); 
$end_time = $end_array[1] + $end_array[0]; 
$time = $end_time - $start_time; 
printf("Время выполения %f секунд",$time); 
?>

Автор: awers 5.3.2008, 15:31
да, все верно ) потестил пустой пхп файл.
Время выполения 0.000233 секунд

Автор: mishaSL 5.3.2008, 17:39
sanich_, я не говорю что мой код оптимальный, я просто привел пример выполнения одним запросом + хотел вам показать что все же лучше разделить логику от вывода информации. Хоть это и не относится к теме.

Добавлено через 2 минуты и 56 секунд
Если вы ходите акцентировать внимание только на скорости, то скиньте дамп БД (прикрепите к посту в архиве) и ради интереса когда у меня появится время я предложу оптимальный вариант по скорости. Думаю 0,14-0,15sec это далеко не предел мечтанийsmile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)