ВходИмяПароль
Автоматически входить при каждом посещении    
Регистрация
Регистрация
Войти и проверить личные сообщения
Войти и проверить личные сообщения
Список форумов ЛанМедиаСервис » Скрипт видео-каталога v.1.x

Начать новую тему   Ответить на тему
Жуткие тормоза
Предыдущая тема :: Следующая тема  
Автор Сообщение

esm@gts.dp.ua





Зарегистрирован: 29.02.2008
Сообщения: 11


blank.gif
Сообщение Добавлено: 2010-11-14 18:56 pm    Заголовок сообщения: Жуткие тормоза Ответить с цитатой

Дано: каталог с фильмами на Вашем движке с 45000+ файлов и 11000+ фильмов. Это единственный сайт, который крутится на сервере с 512МБ ОЗУ и 2,4 ГГц ЦП. И это все безбожно тормозит, когда по вечерам случается наплыв посетителей! (точного количества - не скажу).

Откровенно говоря, на мой вкус, для КАЖДОГО посетителя считать рекомендуемые фильмы через коэффициент корреляции да еще на ПХП (который категорически не предназначен для математически сложных вычислений). Или делать поиск через выборку всех фильмов через расстояние Левенштайна (сложность алгоритма для двух строк - квадратичная, и это для КАЖДОГО названия фильма) - это полнейший капец. + таких "радостей" в коде полным полно.

Но я сейчас о другом. Я бы крайне сильно настаивал, чтобы следующие изменения были внесены в скрипт. Главным образом они связаны с кешированием + отключение функции рекомендации фильмов (если уж на то пошло, то нужно хотя бы дать возможность отключать ее из админки). Таким образом, у пользователей появится возможность не задумываться о покупке нового железа, когда это совершенно не нужно, а у меня - пропадет необходимость что-либо менять после каждого обновления!

изменения:
Вернуться к началу
Посмотреть профиль Отправить личное сообщение

Angel





Зарегистрирован: 29.02.2008
Сообщения: 79


ukraine.gif
Сообщение Добавлено: 2010-11-14 21:30 pm    Заголовок сообщения: Ответить с цитатой

Можете сказать какое ПО стоит на сервере, так как у самого примерно такое же кол-во фильмов и какого либо повышения нагрузки в часы пик не испытываю
Вернуться к началу
Посмотреть профиль Отправить личное сообщение

Илья Спесивцев

Администратор



Зарегистрирован: 26.02.2008
Сообщения: 703
Откуда: Техподдержка

blank.gif
Сообщение Добавлено: 2010-11-15 10:00 am    Заголовок сообщения: Ответить с цитатой

Для отключения блока рекомендаций можно в шаблоне templates/*/main.php убрать строку:
Код:
<div id="RecommendedBox"></div>



Насчет кеширования. Не совсем понял как оно работает - ведь к каждому запросу JsHttpRequest добавляет индивидуальный параметр типа JsHttpRequest=12897720629931-xml, таким образом данные из кеша никогда не используются повторно.

Для увеличения производительности можем также посоветовать:
1. Перейти на nginx + fastcgi (сэкономит память)
2. MySQL перевести на движок innodb и настроить буфер по размеру базы (тогда она вся будет в памяти):
innodb_flush_method=O_DIRECT
innodb_buffer_pool_size=100M
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора

esm@gts.dp.ua





Зарегистрирован: 29.02.2008
Сообщения: 11


blank.gif
Сообщение Добавлено: 2010-11-15 10:21 am    Заголовок сообщения: Ответить с цитатой

Цитата:
добавляет индивидуальный параметр типа JsHttpRequest=12897720629931-xml
Это аналог сессии что ли?

Цитата:
Не совсем понял как оно работает


Сразу после:
Код:
if (getRights($action,$user) || ($action=="exit")){


Код:
    if(in_array($action, array('filmlist', 'gettypesofmovie', 'getcountries', 'getgenres','lastcomments', 'getrndfilm', 'gettoplist', 'lastratings', 'getfilm'))&&($user['UserGroup']==1))
    {
       $hash=md5(serialize($_GET).serialize($_POST));
       $_RESULT=getCache($hash);
       if($_RESULT){ $action='already_loaded_from_cache';}
         else {$_RESULT=array();}
    }

    switch ($action) {
        case "already_loaded_from_cache": break;
        case "check_update":



Ну и в конце:
Код:
    if(in_array($action, array('filmlist', 'gettypesofmovie', 'getcountries', 'getgenres', 'lastcomments', 'getrndfilm', 'gettoplist', 'lastratings', 'getfilm'))&&($user['UserGroup']==1))
    {
       $hash=md5(serialize($_GET).serialize($_POST));
       setCache($hash, $_RESULT);
    }




   
}
else{
    $_RESULT["errors"][] = "Извините, $login, у Вас недостаточно прав для совершения действия '$action' ";
    echo "Извините, $login, у Вас недостаточно прав для совершения действия '$action' ";
}


Добавлено спустя 1 minute 29 seconds:

Ну и поиск фильмов вместо такого:

Код:
        case "simplesearch":
            $text = (isset($_REQUEST['text'])) ? strtolower($_REQUEST['text']) : null;
            $what = (isset($_REQUEST['what'])) ? strtolower($_REQUEST['what']) : "films";
            if (!getRights("show_hidden",$user)) $wherehide = " WHERE films.Hide=0 " . ($itemFilter? "AND $itemFilter" : ''); else $wherehide = $itemFilter? "WHERE $itemFilter" : '';
            if ($text){
                switch ($what){
                    case "films":
                        $ltext = strlen($text);
                        $sql = "SELECT films.ID as ID,
                            films.Name as Name,
                            films.OriginalName as OriginalName,
                            films.Year as Year
                            FROM films $wherehide";
                        $result = mysql_query($sql);
                        $ei = 0;
                        $pi = 0;
                        $ai = 0;
                        $fcount = 0;
                        $films_exact = array();
                        $films_part = array();
                        $films_approx = array();

                        $sortarray = array();
                        while ($result && $field = mysql_fetch_assoc($result)){
                            $name = strtolower($field["Name"]);
                            $originalName = strtolower($field["OriginalName"]);
                            $lev_n = levenshtein($name, $text,2,1,1);
                            $lev_on = levenshtein($originalName, $text,2,1,1);
                            $pos_n = strpos($name,$text);
                            $pos_on = strpos($originalName, $text);
                            $d_n = abs(strlen($name)-$ltext);
                            $d_on = abs(strlen($originalName)-$ltext);
                            $l_n = min($ltext,strlen($name));
                            $l_on = min($ltext,strlen($originalName));

                            if ($lev_n==0 || $lev_on==0){
                                $films_exact[$ei] = $field;
                                $ei++;
                                $fcount++;
                            }
                            else{
                                if (($ltext>1) && (is_integer($pos_n) || is_integer($pos_on))){
                                    $films_part[$pi] = $field;
                                    $pi++;
                                    $fcount++;
                                } elseif (($lev_n<(($l_n*limit_lev($ltext))+$d_n)) || ($lev_on<(($l_on*limit_lev($ltext))+$d_on)) ){
                                    $sortarray[$ai] = min($lev_on - $d_on,$lev_n - $d_n);
                                    $films_approx[$ai] = $field;
                                    $ai++;
                                    $fcount++;
                                }
                            }
                        }
                        array_multisort($sortarray,SORT_ASC,$films_approx);
                        $_RESULT["films_exact"] = $films_exact;
                        $_RESULT["films_part"] = $films_part;
                        $_RESULT["films_approx"] = $films_approx;
                        $_RESULT["fcount"] = $fcount;
                    break;
                    case "persones":
                        $ltext = strlen($text);
                        $sql = "SELECT persones.ID as ID,
                            persones.RusName as RusName,
                            persones.OriginalName as OriginalName
                            FROM persones";
                        $result = mysql_query($sql);
                        $ei = 0;
                        $pi = 0;
                        $ai = 0;
                        $pcount = 0;
                        $persones_exact = array();
                        $persones_part = array();
                        $persones_approx = array();

                        $sortarray = array();
                        while ($result && $field = mysql_fetch_assoc($result)){
                            $lev_n = levenshtein($field["RusName"], $text,2,1,1);
                            $lev_on = levenshtein($field["OriginalName"], $text,2,1,1);
                            $pos_n = strpos(strtolower($field["RusName"]),strtolower($text));
                            $pos_on = strpos(strtolower($field["OriginalName"]),strtolower($text));
                            $d_n = abs(strlen($field["RusName"])-$ltext);
                            $d_on = abs(strlen($field["OriginalName"])-$ltext);
                            $l_n = min($ltext,strlen($field["RusName"]));
                            $l_on = min($ltext,strlen($field["OriginalName"]));

                            if ($lev_n==0 || $lev_on==0){
                                $persones_exact[$ei] = $field;
                                $ei++;
                                $pcount++;
                            }
                            else{
                                if (($ltext>1) && (is_integer($pos_n) || is_integer($pos_on))){
                                    $persones_part[$pi] = $field;
                                    $pi++;
                                    $pcount++;
                                } elseif (($lev_n<(($l_n*limit_lev($ltext))+$d_n)) || ($lev_on<(($l_on*limit_lev($ltext))+$d_on)) ){
                                    $sortarray[$ai] = min($lev_on - $d_on,$lev_n - $d_n);
                                    $persones_approx[$ai] = $field;
                                    $ai++;
                                    $pcount++;
                                }
                            }
                        }
                        array_multisort($sortarray,SORT_ASC,$persones_approx);
                        $c = count($persones_approx);
                        for ($i = 20; $i<$c; $i++) {
                            array_pop($persones_approx);
                            $pcount--;
                        }
                        $_RESULT["persones_exact"] = $persones_exact;
                        $_RESULT["persones_part"] = $persones_part;
                        $_RESULT["persones_approx"] = $persones_approx;
                        $_RESULT["pcount"] = $pcount;
                    break;
                }
            }
        break;


Стал выглядеть вот так:

Код:
        case "simplesearch":
            $text = (isset($_REQUEST['text'])) ? strtolower($_REQUEST['text']) : null;
            $what = (isset($_REQUEST['what'])) ? strtolower($_REQUEST['what']) : "films";
            if (!getRights("show_hidden",$user)) $wherehide = " AND films.Hide=0 " . ($itemFilter? "AND $itemFilter" : ''); else $wherehide = $itemFilter? " AND $itemFilter" : '';
            if ($text){
                switch ($what){
                    case "films":

                        $srch  = mysql_real_escape_string($text);
                        $sql = "SELECT SQL_CALC_FOUND_ROWS `ID`, `Name` , `OriginalName`, `Year` FROM `films`
                                WHERE MATCH ( `Name` , `OriginalName`)
                                AGAINST ('$srch' IN BOOLEAN MODE)
                                $wherehide
                                ORDER BY `id` DESC";
             
                        $result = mysql_query($sql);

                        $films_exact = array();
                        $fcount=0;
                        while ($field = mysql_fetch_assoc($result)){
                           $films_exact[] = $field;
                           $fcount++;
                        }
                        $_RESULT["films_exact"]  = &$films_exact;
                        $_RESULT["films_part"]   = array();
                        $_RESULT["films_approx"] = array();
                        $_RESULT["fcount"] = $fcount;
                    break;
                    case "persones":
                        $srch  = mysql_real_escape_string($text);
                        $sql = "SELECT SQL_CALC_FOUND_ROWS `ID`, `RusName` , `OriginalName` FROM `persones`
                                WHERE MATCH ( `RusName` , `OriginalName`)
                                AGAINST ('$srch' IN BOOLEAN MODE)";

                        $result = mysql_query($sql);
                        $pcount = 0;
                        $persones_exact = array();

                        while ($field = mysql_fetch_assoc($result)){
                           $persones_exact[] = $field;
                           $pcount++;
                        }

                        $_RESULT["persones_exact"]  = &$persones_exact;
                        $_RESULT["persones_part"]   = array();
                        $_RESULT["persones_approx"] = array();
                        $_RESULT["pcount"] = $pcount;
                    break;
                }
            }
        break;

Естественно, с добавлением полнотекстового индекса.

Добавлено спустя 7 minutes 29 seconds:

Код:
Для увеличения производительности можем также посоветовать:
1. Перейти на nginx + fastcgi (сэкономит память)
2. MySQL перевести на движок innodb и настроить буфер по размеру базы (тогда она вся будет в памяти):
innodb_flush_method=O_DIRECT
innodb_buffer_pool_size=100M

Загибался ЦП, насколько я помню, так что база - не при чем. И это не сильно удивительно, если для того чтобы найти фильм - Вы их все выбираете из БД, а потом название каждого фильма сравниваете с введеным запросом вот этой функцией: http://ua2.php.net/manual/en/function.levenshtein.php .

Добавлено спустя 21 minutes 27 seconds:

Код:
Можете сказать какое ПО стоит на сервере, так как у самого примерно такое же кол-во фильмов и какого либо повышения нагрузки в часы пик не испытываю
Обычный LAMP стек. Если никаких проблем, значит пока просто достаточно пользователей не набежало, чтобы убить Ваш ЦП.

Например, вопрос к разработчикам. Можете, пожалуйста, прояснить для чего делать следующие дествия при выборке лишь 1 единственного фильма?
Код:
 $result = mysql_query( "SELECT ID FROM films WHERE Hide=0 ORDER BY Hit DESC");
            $countfilms = mysql_num_rows($result);
            $hits = array();
            $i = 0;
            while ($result && ($field = mysql_fetch_assoc($result)) && ($i<$countfilms/10)){
                $hits[] = $field["ID"];
                $i++;
            }

Кроме как дальнейшей проверки на то хит это или нет - не нашел как оно используется?
Вернуться к началу
Посмотреть профиль Отправить личное сообщение

Илья Спесивцев

Администратор



Зарегистрирован: 26.02.2008
Сообщения: 703
Откуда: Техподдержка

blank.gif
Сообщение Добавлено: 2010-11-15 12:41 pm    Заголовок сообщения: Ответить с цитатой

Я к тому, что приведенная реализация кеширования никак не могла дать какой-либо эффект (т.к. в каждый запрос добавляется уникальный ID запроса).
Насчет поиска - да он довольно тяжелый, но и используется он обычно не очень часто, чтобы стать ключевой проблемой в производительности. Можете убедится помониторив у себя лог запросов:
Код:
tail -f http-access.log |grep search


Цитата:
Например, вопрос к разработчикам. Можете, пожалуйста, прояснить для чего делать следующие дествия при выборке лишь 1 единственного фильма?

Да, и это, в принципе, дешевый запрос (4 байт * 11000 = всего 44 КБ данных), если хорошо работает кеширование в MySQL и ему не приходится постоянно обращаться к диску.

Вообщем, не вижу смысла убирать функции, которые никому из других клиентов не мешают. Но вы, конечно, можете править любой участок кода так, как считаете нужным. Со временем мы планируем сделать возможность обновляться через svn, чтобы сделанные изменения не затирались при нем.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора

esm@gts.dp.ua





Зарегистрирован: 29.02.2008
Сообщения: 11


blank.gif
Сообщение Добавлено: 2010-11-15 13:15 pm    Заголовок сообщения: Ответить с цитатой

Ну да, именно поэтому загрузка ЦП упала со 100%+ до 20%...

Чисто из любопытства, прежде чем говорить, что это не работает - могли бы поставить и проверить. (заменить файл не так уж и сложно)

Добавлено спустя 11 minutes 2 seconds:

И вдогонку: это в Мускуле - 44КБ, потом Вы загоняете это дело в ассоциированный массив ПХП (который построен на базе бинарного дерева и имеет уже тип не инт, а динамическое типизирование). Т.е. в ПХП это уже будет по крайней мере 200-300 КБ, а потом еще и поиск делаете in_array, т.е. делаете 11000 сравнений. Да один этот поиск будет работать дольше, чем все преобразования с данными до и после него.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение

Илья Спесивцев

Администратор



Зарегистрирован: 26.02.2008
Сообщения: 703
Откуда: Техподдержка

blank.gif
Сообщение Добавлено: 2010-11-15 14:09 pm    Заголовок сообщения: Ответить с цитатой

esm@gts.dp.ua писал(а):
Ну да, именно поэтому загрузка ЦП упала со 100%+ до 20%...

Чисто из любопытства, прежде чем говорить, что это не работает - могли бы поставить и проверить. (заменить файл не так уж и сложно)

Файл memcache.php в архиве не открывается. Но я проверил по-другому - вы правы параметр JsHttpRequest удаляется и потом не мешает кешированию.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора

esm@gts.dp.ua





Зарегистрирован: 29.02.2008
Сообщения: 11


blank.gif
Сообщение Добавлено: 2010-11-15 14:14 pm    Заголовок сообщения: Ответить с цитатой

memcache.php
Код:
<?php
function &getCache($hash, $valid=900)
{
  $memcache = new Memcache;
  $memcache->connect('localhost', 11211);

  return $memcache->get($hash);
}

function setCache($hash, &$data, $valid=900)
{
  $memcache = new Memcache;
  $memcache->connect('localhost', 11211);

  return $memcache->set($hash, $data, 0, $valid);
}

?>


Добавлено спустя 18 hours 58 minutes 24 seconds:

Цитата:
Вообщем, не вижу смысла убирать функции, которые никому из других клиентов не мешают.
Мне кажется, что было бы здорово увидеть в админке:
1. список "галочек", какие блоки выводить, а какие - нет
2. настройку по включению, отключению кеширования.
3. Возможность выбирать из поиска Вашим способ и моим, для сайтов, которым железа начало не хватать, а тратить 800+ у.е. на новый сервер - не хочется.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение

sasuke





Зарегистрирован: 29.04.2010
Сообщения: 114
Откуда: Гомель/Новополоцк

belarus.gif
Сообщение Добавлено: 2010-11-16 23:19 pm    Заголовок сообщения: Ответить с цитатой

на 1.05 не пошло (((
Вернуться к началу
Посмотреть профиль Отправить личное сообщение

esm@gts.dp.ua





Зарегистрирован: 29.02.2008
Сообщения: 11


blank.gif
Сообщение Добавлено: 2010-11-17 09:14 am    Заголовок сообщения: Ответить с цитатой

Быть может, у Вас мемкеш на сервере не стоит.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение

sasuke





Зарегистрирован: 29.04.2010
Сообщения: 114
Откуда: Гомель/Новополоцк

belarus.gif
Сообщение Добавлено: 2010-11-17 11:55 am    Заголовок сообщения: Ответить с цитатой

может...это я чуть позже узнаю... а как проверить стоит он или нет и если нет то как запустить/установить?
Вернуться к началу
Посмотреть профиль Отправить личное сообщение

esm@gts.dp.ua





Зарегистрирован: 29.02.2008
Сообщения: 11


blank.gif
Сообщение Добавлено: 2010-11-21 14:14 pm    Заголовок сообщения: Ответить с цитатой

Чтобы попробовать, как оно будет работать с кешем, можно для начала сделать кеш на файлах:

Код:
<?php
$CachePath=dirname(__FILE__).'/cachedir';

function &getCache($hash, $valid=900)
{ global $CachePath;

   $filename=$CachePath.'/'.$hash.'.txt';
   /* Попытка чтения кэш-файла */
   if (@$file=fopen($filename, 'r')){
     if(filemtime($filename)>(time()-$valid)){
     //  flock($file, LOCK_SH);
       $serial=file_get_contents($filename);
       $ResultData=unserialize($serial);
       fclose($file);
       
       return $ResultData;
     }
    // else
     //unlink($filename);
   }
   
   if ($file){
      fclose($file);
   }
return false;
}

function setCache($hash, &$data)
{ global $CachePath;

   $filename=$CachePath.'/'.$hash.'.txt';
   /* Запись кэша */
   $file=fopen($filename, 'w');
   flock($file, LOCK_EX);
   fwrite($file, serialize($data));
   fclose($file);
}

?>


Только в корне скрипта фильмов нужно создать папку "cachedir" с правами на запись, а также периодически ее вручную чистить.

Относительно мемкеша - лучше обратиться к администратору Вашего сервера.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение

esm@gts.dp.ua





Зарегистрирован: 29.02.2008
Сообщения: 11


blank.gif
Сообщение Добавлено: 2010-12-12 20:31 pm    Заголовок сообщения: Ответить с цитатой

Чтобы не разводить темы. Нашел еще одну причину возможных тормозов (в нашем случае без "возможно"): функция "Найти похожие фильмы".

В нашем случае в таблице hits было более 3000000 записей и перемножалась эта таблица на таблицу фильмов (11000 записей). В результате серверу не хватало ОЗУ, начинался своп и т.д. Как следствие - сервер ложился на минутку-другую.

Решение:
actions.php -
case "similar_films":
die();
...

case "similar_films_by_rating":
die();


Это предотвращает запросы вроде такого:

Код:
SELECT FilmID, Name, count(*)/$users_count as c, count(*)/films.Hit as rank
FROM hits
     LEFT JOIN
     films
     ON (films.ID = hits.FilmID)
WHERE UserID in (".implode(",",$users).") $where
GROUP BY FilmID HAVING c>$minpercent AND rank>$minrank
ORDER BY rank DESC LIMIT 5


Добавлено спустя 27 minutes 18 seconds:

Ну и исключительно для разработчиков скрипта, во что выливается этот запрос в чистом SQL-коде:
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Показать сообщения:   
Начать новую тему   Ответить на тему    Список форумов ЛанМедиаСервис » Скрипт видео-каталога v.1.x Часовой пояс: GMT
Страница 1 из 1

 
Перейти:  
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете вкладывать файлы
Вы не можете скачивать файлы