|
Предыдущая тема :: Следующая тема |
Автор |
Сообщение |
esm@gts.dp.ua
Зарегистрирован: 29.02.2008 Сообщения: 11
|
Добавлено: 2010-11-14 18:56 pm Заголовок сообщения: Жуткие тормоза |
|
|
Дано: каталог с фильмами на Вашем движке с 45000+ файлов и 11000+ фильмов. Это единственный сайт, который крутится на сервере с 512МБ ОЗУ и 2,4 ГГц ЦП. И это все безбожно тормозит, когда по вечерам случается наплыв посетителей! (точного количества - не скажу).
Откровенно говоря, на мой вкус, для КАЖДОГО посетителя считать рекомендуемые фильмы через коэффициент корреляции да еще на ПХП (который категорически не предназначен для математически сложных вычислений). Или делать поиск через выборку всех фильмов через расстояние Левенштайна (сложность алгоритма для двух строк - квадратичная, и это для КАЖДОГО названия фильма) - это полнейший капец. + таких "радостей" в коде полным полно.
Но я сейчас о другом. Я бы крайне сильно настаивал, чтобы следующие изменения были внесены в скрипт. Главным образом они связаны с кешированием + отключение функции рекомендации фильмов (если уж на то пошло, то нужно хотя бы дать возможность отключать ее из админки). Таким образом, у пользователей появится возможность не задумываться о покупке нового железа, когда это совершенно не нужно, а у меня - пропадет необходимость что-либо менять после каждого обновления!
изменения: |
|
Вернуться к началу |
|
Angel
Зарегистрирован: 29.02.2008 Сообщения: 79
|
Добавлено: 2010-11-14 21:30 pm Заголовок сообщения: |
|
|
Можете сказать какое ПО стоит на сервере, так как у самого примерно такое же кол-во фильмов и какого либо повышения нагрузки в часы пик не испытываю |
|
Вернуться к началу |
|
Илья Спесивцев Администратор
Зарегистрирован: 26.02.2008 Сообщения: 703 Откуда: Техподдержка
|
Добавлено: 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 |
|
Вернуться к началу |
|
esm@gts.dp.ua
Зарегистрирован: 29.02.2008 Сообщения: 11
|
Добавлено: 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 Откуда: Техподдержка
|
Добавлено: 2010-11-15 12:41 pm Заголовок сообщения: |
|
|
Я к тому, что приведенная реализация кеширования никак не могла дать какой-либо эффект (т.к. в каждый запрос добавляется уникальный ID запроса).
Насчет поиска - да он довольно тяжелый, но и используется он обычно не очень часто, чтобы стать ключевой проблемой в производительности. Можете убедится помониторив у себя лог запросов:
Код: | tail -f http-access.log |grep search |
Цитата: | Например, вопрос к разработчикам. Можете, пожалуйста, прояснить для чего делать следующие дествия при выборке лишь 1 единственного фильма? |
Да, и это, в принципе, дешевый запрос (4 байт * 11000 = всего 44 КБ данных), если хорошо работает кеширование в MySQL и ему не приходится постоянно обращаться к диску.
Вообщем, не вижу смысла убирать функции, которые никому из других клиентов не мешают. Но вы, конечно, можете править любой участок кода так, как считаете нужным. Со временем мы планируем сделать возможность обновляться через svn, чтобы сделанные изменения не затирались при нем. |
|
Вернуться к началу |
|
esm@gts.dp.ua
Зарегистрирован: 29.02.2008 Сообщения: 11
|
Добавлено: 2010-11-15 13:15 pm Заголовок сообщения: |
|
|
Ну да, именно поэтому загрузка ЦП упала со 100%+ до 20%...
Чисто из любопытства, прежде чем говорить, что это не работает - могли бы поставить и проверить. (заменить файл не так уж и сложно)
Добавлено спустя 11 minutes 2 seconds:
И вдогонку: это в Мускуле - 44КБ, потом Вы загоняете это дело в ассоциированный массив ПХП (который построен на базе бинарного дерева и имеет уже тип не инт, а динамическое типизирование). Т.е. в ПХП это уже будет по крайней мере 200-300 КБ, а потом еще и поиск делаете in_array, т.е. делаете 11000 сравнений. Да один этот поиск будет работать дольше, чем все преобразования с данными до и после него. |
|
Вернуться к началу |
|
Илья Спесивцев Администратор
Зарегистрирован: 26.02.2008 Сообщения: 703 Откуда: Техподдержка
|
Добавлено: 2010-11-15 14:09 pm Заголовок сообщения: |
|
|
esm@gts.dp.ua писал(а): | Ну да, именно поэтому загрузка ЦП упала со 100%+ до 20%...
Чисто из любопытства, прежде чем говорить, что это не работает - могли бы поставить и проверить. (заменить файл не так уж и сложно)
|
Файл memcache.php в архиве не открывается. Но я проверил по-другому - вы правы параметр JsHttpRequest удаляется и потом не мешает кешированию. |
|
Вернуться к началу |
|
esm@gts.dp.ua
Зарегистрирован: 29.02.2008 Сообщения: 11
|
Добавлено: 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 Откуда: Гомель/Новополоцк
|
Добавлено: 2010-11-16 23:19 pm Заголовок сообщения: |
|
|
на 1.05 не пошло ((( |
|
Вернуться к началу |
|
esm@gts.dp.ua
Зарегистрирован: 29.02.2008 Сообщения: 11
|
Добавлено: 2010-11-17 09:14 am Заголовок сообщения: |
|
|
Быть может, у Вас мемкеш на сервере не стоит. |
|
Вернуться к началу |
|
sasuke
Зарегистрирован: 29.04.2010 Сообщения: 114 Откуда: Гомель/Новополоцк
|
Добавлено: 2010-11-17 11:55 am Заголовок сообщения: |
|
|
может...это я чуть позже узнаю... а как проверить стоит он или нет и если нет то как запустить/установить? |
|
Вернуться к началу |
|
esm@gts.dp.ua
Зарегистрирован: 29.02.2008 Сообщения: 11
|
Добавлено: 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
|
Добавлено: 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-коде: |
|
Вернуться к началу |
|
|
|
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете голосовать в опросах Вы не можете вкладывать файлы Вы не можете скачивать файлы
|
|