Оптимизация PHP
Существует ряд правил, которые стоит соблюдать, чтобы увеличить скорость работы приложений на PHP. Правила простые и не потребуют значительных изменений в существующих приложениях.
Fastcgi
FastCGI — это один из вариантов подключения PHP к Web серверу. Лучше всего использовать в связке с Nginx. PHP-fpm (Fastcgi контейнер для PHP) и Nginx по умолчанию поддерживают совместную работу и очень легко настраиваются.
OpCache
Как обычно выполняется PHP скрипт? PHP открывает файл с кодом, компилирует его, затем выполняет. Поскольку файлов может быть много, процесс их открытия, чтения и компиляции может отнимать кучу ресурсов. Если файлы не меняются, то постоянную компиляцию можно и не делать. Лучше сделать ее один раз и закэшировать результат.
Именно это и делает модуль opCache. Результат первой компиляции будет сохранен в кэш, с которым и будет работать PHP. Таким образом это ускорит выполнение за счет отсутствия тяжелого процесса компиляции. Когда файлы изменятся, модуль сам сбросит кэш и обеспечит перекомпиляцию. Короче, этот модуль делает очень полезную экономию ресурсов. И это без необходимости его настраивать.
В версии PHP5.5+ этот модуль поставляется в стандартной сборке. В предыдущих версиях модуль нужно устанавливать самостоятельно. Проверить наличие можно так:
php -i | grep opcache
Пустой вывод будет означать, что модуля нет
Если версия слишком ранняя, лучше использовать APC:
apt-cache search php-apc
Это альтернатива opCache, но делает то же самое
Кэширование
Часто код просто медленный. Например:
- обращения к внешним API
- тяжелые выборки из баз данных
- обработка больших файлов
В этом случае, кэширование данных следует использовать как средство оптимизации.
Для PHP наиболее популярным решением для кэширования является Memcache. Очень простой в использовании и очень быстрый, т.к. поддерживает только самое необходимое.
PHP.ini
Если Вы только установили PHP, убедитесь, что Вы настроили наиболее важные параметры под Ваш сайт. Это также может сэкономить ресурсы:
memory_limit = 32M
— не стоит устанавливать этот параметр слишком большим. Увеличивайте его только в крайних случаях.zlib.output_compression = Off
,zlib.output_compression_level = -1
— компрессию лучше использовать на стороне Web сервера.max_execution_time = 5
— максимальное время работы скрипта не должно быть больше 5 секунд. Увеличивайте только в крайних случаях.zend.enable_gc = On
— включает сборщик мусора (будет оптимизировать память на фоне).expose_php = Off — PHP
не будет отправлять свою версию вместе с ответом.report_memleaks = On
— будет отправлять в лог ошибок информацию об обнаруженных утечках памяти.post_max_size = 4M
,upload_max_filesize = 4M
— настройте максимальный размер запросов и файлов для загрузки. Защитит от обработки громадных запросов, которых не должно быть в приложении.
Сессии
По умолчанию, PHP хранит сессии в файлах. Это довольно эффективное решение. Но когда файлов становится очень много (десятки тысяч), работа с ними будет замедляться в рамках одной папки (особенности файловых систем). В этом случае лучше перенести сессии на Memcache (php.ini):
session.save_handler = memcache session.save_path = "tcp://localhost:11211" localhost:11211 это стандартный хост и порт Memcache
Кроме этого, такая схема хранения позволит масштабироваться на несколько серверов.
Оптимизация кода
ООП
Помните! ООП — это всегда медленно. Объекты нужно создавать, где-то хранить и уничтожать. Не используйте объекты, если они не нужны. Например, тут:
<?
$post = new Post();
$post->set_title($_GET['title']);
$post->set_description($_GET['description']);
$post->save();
Создаем объект только для того, чтобы сохранить данные в БД
<? # $posts = список объектов Post, полученных каким-то образом foreach ( $posts as $post ) { echo $post->title . '<br/>'; }
Используем список объектов только для того, чтобы вывести свойство
В этих примерах использование ООП не имеет особого смысла. Зато расходует ресурсы. Старайтесь использовать массивы, когда объекты не нужны.
<? mysql::insert(['title' => $_GET['title'], 'description' => $_GET['description']]);
Избежали создания объекта, функция просто сохраняет данные из массива в базу или тут:
$posts = mysql::query('SELECT title FROM posts'); foreach ( $posts as $post ) { echo $post['title'] . '<br/>'; }
Намного лучше — сделать простую выборку и вывести нужные данные из массива
Мелочи
При работе с файлами используйте абсолютные пути. Тогда не будут происходить лишние операции поиска файла:
<? include 'file.php'; file_get_contents('dir/data.txt'); include '/var/www/file.php'; file_get_contents('/var/www/dir/data.txt');
Константы классов работают эффективнее, чем define:
<? define('MAX_POSTS_PER_PAGE', 10); class posts { const PER_PAGE = 10; ...
Не используем функции в условии for, т.к. они будут повторяться на каждой итерации цикла:
<? for ( $i = 0; $i < mysql::get_col('SELECT count(*) FROM posts'); $i++ ) { ... } $max = mysql::get_col('SELECT count(*) FROM posts'); for ( $i = 0; $i < $max; $i++ ) { ... }
В качестве ключей массивов всегда указывайте строки с кавычками:
<? $post[title] = 'Первый пост'; $post['title'] = 'Первый пост';
Используйте встроенные функции работы со строками вместе регулярных выражений, если это возможно.
<? preg_match('/хорошо/ui', $post['title']); strpos($post['title'], 'хорошо');
Используйте строки с одинарными кавычками:
<? $post["title"] = "Почему?"; $post['title'] = 'В этом случае нет дополнительной обработки переменных'
PHP cron-скрипты
Когда PHP используется для разработки скрипта, который будет выполняться по крону, следует избегать использования глобальных переменных. Частый пример, это просто использование общего массива:
<?
while ( true )
{
$rss = file_get_contents('http://somesite.com/rss');
preg_match_all('/title>(.+?)<\/title/', $rss, $matches);
}
Переменная $matches
передается по ссылке. Это значит, что с каждым новым повторением, она будет расти. Другой частый пример, это просто использование общего массива:
<? while ( true ) { $rss = file_get_contents('http://somesite.com/rss'); $has_something = preg_match('/title>(.+?)<\/title/', $rss); if ( $has_something ) $updates[] = time(); $rss = file_get_contents('http://othersource.com/rss'); $has_something = preg_match('/title>(.+?)<\/title/', $rss); if ( $has_something ) $updates[] = time(); }
Теперь переменная $updates
будет расти до максимального предела. Когда будет достигнут лимит по памяти, скрипт будет остановлен. Уследить за всеми переменными довольно тяжело, поэтому лучше использовать функции. Все переменные, созданные внутри функции будут удаляться после ее завершения:
<? while ( true ) process(); function process() { $rss = file_get_contents('http://somesite.com/rss'); $has_something = preg_match('/title>(.+?)<\/title/', $rss); if ( $has_something ) $updates[] = time(); $rss = file_get_contents('http://othersource.com/rss'); $has_something = preg_match('/title>(.+?)<\/title/', $rss); if ( $has_something ) $updates[] = time(); }
Самое важное
- Обязательно используйте opCache для PHP. Это бесплатно экономит ресурсы.
- Используйте FastCGI (лучше всего Nginx + PHP-fpm).
- Функции в крон задачах помогут избежать утечек памяти.
- Кэширование медленных участков кода часто самое простое решение для ускорения работы.
- Помните о важных мелочах.
Этот текст был написан несколько лет назад. С тех пор упомянутые здесь инструменты и софт могли получить обновления. Пожалуйста, проверяйте их актуальность.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: