Общие вопросы

Проблема нагрузки на серверах виртуального хостинга

 

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

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

Существует два основных способа создать нагрузку на сервер виртуального хостинга: нагрузка на процессор и нагрузка на сервер БД MySQL.

Нагрузка на процессор

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

Нагрузка, создаваемая скриптами

Самый распространенный вопрос клиента:
Я продолжительное время не изменял скрипты и не обновлял сайт. Почему вдруг стала возникать нагрузка на сервер? Ответов может быть несколько:

 

  1. Произошло какое-либо нарушение в структуре базы данных, которую использует ваш сайт.
  2. Произошла ошибка в системе кэширования вашего сайта, что создало большое количество страниц, и при обращении поисковой системы создается нагрузка.
  3. Другие причины: возросла посещаемость вашего ресурса.

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

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

Нагрузка, создаваемая поисковыми системами

Почему?

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

Что делать?

Первым делом стоит посмотреть, сколько было совершено запросов к вашему сайту. Для этого вам необходимо авторизоваться на сервере, используя ssh клиент, и выполнить команду:

  • для yandex:
    grep 22/Oct/2008 /usr/local/apache/domlogs/example.org | grep Yandex | wc -l
  • для Google:
    grep 29/Apr/2008 /usr/local/apache/domlogs/example.org | grep www.google.com/bot.html | wc -l

Также можно использовать вместо Yandex и www.google.com/bot.html идентификаторы других поисковых систем, узнать которые можно, обратившись в службу поддержки необходимой поисковой системы либо изучив лог доступа.

Вместо example.org необходимо написать имя домена, расположенного на вашем аккаунте и о котором вы желаете получить информацию; вместо 22/Oct/2008 необходимо указать текущую дату. Иногда можно посмотреть, сколько было выполнено поисковых запросов и за предыдущее число, это зависит от ротации (обнуления) логов доступа. Помимо этого, вы можете настроить в панели управления архивацию логов доступа в домашнюю директорию вашего аккаунта для дальнейшего изучения. Для этого необходимо перейти в раздел "Управление необработанными лог-файлами" Cpanel вашего хостинга и указать "Архивировать журналы в домашнем каталоге в конце каждого месяца", после этого сохранить изменения. Также для анализа логов доступа можно использовать различные программы анализа веб логов. Например, можно использовать программу WebLog Expert.

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

  • Создать, если не был создан ранее, файл robots.txt в папке public_html и прописать в него следующие строки:
    User-agent: Yandex
    Crawl-delay: 10

    задает таймаут в 10 секунд, чтобы робот яндекса следующий запрос задавал не ранее, чем через 10 сек.

    Также можно добавить правила и для других роботов, например, для Google:

    User-agent: Google
    Crawl-delay: 10

    Запретить индексацию некоторых совсем не нужных каталогов, например, каталогов с картинками, административную часть сайта и т.д., можно так:

    User-agent: Google
    Disallow: /images/
    Disallow: /admin/
    Disallow: /forum/

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

  • Вторым необходимым действием является создания карты сайта (sitemap) и дальнейшей работы.

    Файл Sitemap - это файл с дополнительной информацией о страницах сайта, подлежащих индексации. С помощью файла Sitemap вы можете сообщить поисковику, какие страницы вашего сайта нужно индексировать, как часто обновляется информация на страницах, а также, индексация каких страниц наиболее важна. 
    Если вы используете какую-либо CMS систему, часто данные системы уже наделены необходимым функционалом (создания карты сайта). Вам остается его активировать и создать файл sitemap. Если же ваша система не поддерживает создания карты сайта, то можно использовать для этого специальные утилиты. Например, программный продукт от google, ориентированный специально на создание sitemap.

    Если ваш сайт имеет не слишком много страниц - до 500, то можно использовать online генерацию sitemap.

    Для того чтобы указать поисковикам созданную карту сайта, можно также добавить в robots.txt следующее:

    User-agent: *
    Allow: /
    Sitemap: http://example.org/my_sitemaps1.xml

Нагрузка, вызванная обновлением/изменением скриптов

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

Нагрузка, вызванная нехваткой ресурсов для выполнения скрипта

Если в письмах о нагрузке мы сообщаем о том, что какой-либо скрипт был завершен принудительно системой, в связи с достижением лимита на использование процессора, то вам необходимо определить, при каких обстоятельствах это происходит. Обычно подобное происходит на CMS, где несущим файлом является index.php, через который осуществляется подгрузка всех модулей сайта и самого контента. В этом случае вам стоит проанализировать подключенные модули и, по возможности, отказаться от использования наиболее "тяжелых", отключив их в системе. Если принудительно завершенным файлом является не файл CMS, а любой другой файл вашей системы, то необходимо проанализировать код, содержащийся в нем. Возможно, где-то можно сократить вызовы функций, где-то упростить вычисления, где-то сделать запрос к базе "легче" и т.д. Возможно, какие-то участки кода вообще не имеют значения для генерации страницы, например, про них просто забыли, а они работают и используют ресурсы напрасно. Исследуйте код на наличие простых ошибок; бесконечная рекурсия SSI; условие, которое должно было бы ограничивать перебор миллиона вариантов, но не ограничивает из-за ошибки; вечный редирект mod_rewrite... Можете совсем отказаться от использования этого файла на сервере.

Если ваш сайт тратит много времени на генерацию страниц, а содержание страниц изменяется редко, то можно один раз сгенерировать страницу и сохранить её в файл, а при новых обращениях отдавать данные из этого файла - кэширование. Обычно данная функция уже встроена во многих CMS системах. Кэширование необходимо включить с максимальным временем жизни кэша.

Другие причины повышения нагрузки на процессор

Также существуют пользователи сети Интернет, которые выкачивают себе на локальный компьютер сайт полностью, для дальнейшего изучения. Подобные программы называют "оффлайки" или "качалки сайтов". Отличаются данные программы тем, что за короткое время полностью выкачивают сайт, при их работе, серверу дается сразу несколько запросов, потому, что загрузка сайта происходит в несколько потоков, как писалось выше, для быстрого закачивания. Против этих "проделок" созданы лимиты отдачи страниц. Данный метод был разработан и впервые применен Андреем Якушевым.

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

Нагрузка на сервер БД MySQL

Если на вашем сайте используется какая-либо CMS система, то, как правило, в подобных системах работа с базами данных продумана до мелочей, но это не всегда является гарантией того, что нагрузки не будет. Как же это решить? Вы можете попытаться самостоятельно оптимизировать запросы к БД либо попросить разработчика сделать это. Также, какой бы совершенной ни была структура базы данных, рано или поздно она начнет создавать нагрузку на сервер. Это происходит из-за накопления различного "мусора" в базах данных, и тогда, когда не выполняется регулярная оптимизация, под которой понимается чистка "дыр" (дефрагментация), обновление статистики и сортировка индексов. Сделать это можно, авторизовавшись на сервере, используя ssh клиент, и выполнив команду:

mysqlcheck --repair --analyze --optimize --all-databases --auto-repair -u login .ppass

Это следует делать как минимум раз в месяц.

Если же вы используете специально разработанный под ваши нужды сайт с собственной структурой баз данных, то в этом случае необходимо оптимизировать ваши запросы. Методы оптимизации в каждом случае могут быть свои и их множество. Рассмотрим самые распространенные примеры и ошибки, связанные с работой с базами данных в виде "симптом/ошибка - решение":


SELECT
v.video_id
a.name,
g.genre
FROM
videos AS v
LEFT JOIN
link_actors_videos AS la ON la.video_id = v.video_id
LEFT JOIN
actors AS a ON a.actor_id = la.actor_id
LEFT JOIN
link_genre_video AS lg ON lg.video_id = v.video_id
LEFT JOIN
genres AS g ON g.genre_id = lg.genre_id

Нужно помнить, что при связи таблиц "один-ко многим" количество строк в выборке будет расти при каждом очередном JOIN'е. Для подобных случаев целесообразнее бывает разбить подобный запрос на несколько простых.

  • Выборка всех полей
    SELECT * FROM table;

    При создании запросов избегайте, по возможности, выборки всех полей. Перечислите только те поля, которые вам действительно нужны. Кроме этого, не забывайте про покрывающие индексы. Даже если вам не необходимы все поля в таблице, лучше их перечислить. Во-первых, воспринимать код становится легче; во-вторых, со временем количество столбцов в вашей таблице может увеличиваться, соответственно выборка также будет расти.

  • Запросы в цикле

    1. Выборки

    $news_ids get_list('SELECT news_id FROM today_news');
    while ($news_id = get_next($news_ids)) 
    $news[] = get_row('SELECT title, body FROM news WHERE news_id = '.$news_id);

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

    SELECT title, body FROM today_news INNER JOIN news USING(news_id);

    2. Вставки

    $log = parse_log();
    while ($record = next($log))
    query('INSERT INTO logs SET value = '.$log['value']);

    Гораздо более эффективно "склеить" и выполнить один запрос:

    INSERT INTO logs (value) VALUES (...), (...);

    3. Обновления

    Иногда бывает нужно обновить несколько строк в одной таблице. Если обновляемое значение одинаковое, то все просто:

    UPDATE news SET title = 'test' WHERE id IN (1, 2, 3);

    Если изменяемое значение для каждой записи разное, то это можно сделать таким запросом:

    UPDATE news SET
    title = CASE
    WHEN news_id = 1 THEN 'aa'
    WHEN news_id = 2 THEN 'bb' END
    WHERE news_id IN (1, 2);

    Наши тесты показывают, что такой запрос выполняется в 2-3 раза быстрее, чем несколько отдельных запросов.

  • Выполнение операций над проиндексированными полями:
    SELECT user_id FROM users WHERE blogs_count * 2 = $value;

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

    SELECT user_id FROM users WHERE blogs_count = $value/2;

    Аналогичный пример:

    SELECT user_id FROM users WHERE TO_DAYS(CURRENT_DATE- TO_DAYS(registered) <= 10;

    не будет использовать индекс по полю registered, тогда как

    SELECT user_id FROM users WHERE registered >= DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY);

    будет.

  • Выборка строк только для подсчета их количества
    $result = mysql_query("SELECT * FROM table", $link);
    $num_rows = mysql_num_rows($result);

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

    SELECT COUNT(*) FROM table;

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

  • Выборка лишних строк
    $result = mysql_query("SELECT * FROM table1", $link);
    while ($row = mysql_fetch_assoc($result) && $i < 20) {
    ...
    }

    Если вам нужны только n строк выборки, используйте LIMIT, вместо того, чтобы отбрасывать лишние строки в приложении.

  • Использование ORDER BY RAND()
    SELECT * FROM table ORDER BY RAND() LIMIT 1;

    Если в таблице больше, чем 4-5 тысяч строк, то

    ORDER BY RAND()
    будет работать очень медленно. Гораздо более эффективно будет выполнить два запроса:

    Если в таблице auto_increment'ный первичный ключ и нет пропусков:

    $rnd = rand(1query('SELECT MAX(id) FROM table'));
    $row = query('SELECT * FROM table WHERE id = '.$rnd);

    либо:

    $cnt = query('SELECT COUNT(*) FROM table');
    $row = query('SELECT * FROM table LIMIT '.$cnt.', 1');

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

  • Использование большого количества JOIN'ов:
  • Использование LIMIT
    SELECT ... FROM table LIMIT $start, $per_page;

    Многие думают, что подобный запрос вернет $per_page записей (обычно 10-20) и поэтому сработает быстро. Он и сработает быстро для нескольких первых страниц. Но если количество записей велико, и нужно выполнить запрос

    SELECT ... FROM table LIMIT 1000000, 1000020
    то для выполнения такого запроса MySQL сначала выберет 1000020 записей, отбросит первый миллион и вернет 20. Это может быть вовсе не быстро. Тривиальных путей решения проблемы нет. Многие просто ограничивают количество доступных страниц разумным числом. Также можно ускорить подобные запросы использованием покрывающих индексов или сторонних решений (например, sphinx).
  • Использование ON DUPLICATE KEY UPDATE:
    $row = query('SELECT * FROM table WHERE id = 1');
    if ($row)
    query('UPDATE table SET column = column + 1 WHERE id=1')
    else
    query('INSERT INTO table SET column = 1, id=1');

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

    INSERT INTO table SET column = 1, id = 1 ON DUPLICATE KEY UPDATE column = column + 1;
Вернуться Назад