H Оптимизация связки Nginx, Apache, PHP, MySql. Nginx оптимизация
Nginx конфигурационный файл для работы при высоких нагрузках
Nginx - самый популярный веб-сервер, применяемый в системах, обслуживающих нагруженные веб проекты. Nginx выбирают из-за высокой скорости отдачи контента и возможности его использования для проксирования.
Можно использовать конфигурацию по умолчанию и дефолтные виртуальные хосты (которые копируются, переименовываются и в которых заменяют имя сайта и каталог, к которому следует обращаться). Для Nginx конфигурационный файл /etc/nginx/nginx.conf является основным, в нем подключаются все остальные. Путем добавления нужных директив и подбора их значений выполняется тонкая настройка .
Для нагруженных проектов усилия на это затраченные оправдывают себя.
Будем рассматривать тонкую настройку веб-севрера Nginx, работающего на сервере под управлением операционной системы Debian.
Nginx конфигурационный файл
Основной конфигурационный файл имеет следующую структуру:
user www-data; worker_processes 4; pid /run/nginx.pid;
events { worker_connections 768; # multi_accept on; }
http {
sendfile on;tcp_nopush on;tcp_nodelay on;keepalive_timeout 65;types_hash_max_size 2048;
...
}
Для приведенного файла запускается веб-сервер от имени пользователя www-data. Максимальное количество возможных соединений определяется произведением значений worker_processes и worker_connections - каждый воркер может обрабатывать количество соединений, заданное в соответствующей директиве конфига (в примере значение worker_connections равно 768, и задано 4 воркера).
Стандартное для веб-серверов правило - устанавливать количество воркеров в соответствии с количеством ядер процессора для nginx не критично, также значение worker_processes можно устанавливать в auto, но эта настройка актуальна не для всех версий Nginx.
Следующая директива важна для снятия ограничений количества возможных соединений - она находится в блоке events и по-умолчанию закомментирована, чтобы активировать директиву достаточно снять знак комментария и перезапустить Nginx
multi_accept on;
Директивы, задающиеся в разделе конфигурационного файла http
Метод отправки информации
sendfile on;
Указание на необходимость отправки заголовков вместе с данными, что позволит ускорить процесс обработки запросов
tcp_nodelay on;
tcp_nopush on;
Самым важным для быстродействия является включение сжатия - производится обычно также в разделе http
gzip on;
gzip_disable "msie6";
gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json application/javascript;
При активных директивах будет использоваться GZIP сжатие, что покажет GooglePageSpeedInsights
Логирование подключается директивами в секции http, но может также задаваться и в файлах виртуальных хостов
access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log;
Полностью конфигурационный файл может выглядеть так:
user www-data;worker_processes 4;pid /run/nginx.pid;
events { worker_connections 768; # multi_accept on;}
http {
sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048;
server_names_hash_bucket_size 64;
include /etc/nginx/mime.types; default_type application/octet-stream;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log;
gzip on; gzip_disable "msie6"; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*;
}
После внесения изменений в конфигурационный файл необходимо протестировать конфигурацию
nginx -t
Если ошибок в консоль не вывелось - веб-сервер можно перезапускать
service nginx restart
Nginx хорош тем, что универсален. Он легко масштабируется и может проксировать запросы на несколько бэкендов отслеживая их доступность. Nginx применяется для почтовых серверов и является универсальным фронтэндом, что позволяет использовать его в распределенных отказоустойчивых системах.
server-gu.ru
Оптимизация настройки NGINX / DEVACADEMY
NGINX
NGINX - это быстродействующая замена медленному Apache2. Конечно, NGINX, как и любой другой веб-сервер требует корректной настройки для лучшей производительности.
Требования
- Свежеустановленный и настроенный Debian 7 или Ubuntu 12 (13).
- Установленный и настроенный NGINX сервер.
- Понимание основ настройки Linux
Рабочие процессы и рабочие соединения
Первые две переменные, которые необходимо настроить это рабочие процессы и рабочие соединения. Сначала разберемся за что они отвечают. worker_process - основа настройки и жизнедеятельности NGINX. Эта переменная указывает допустимое количество процессов привязанных к определенному IP адресу и порту. Обычно допускают один процесс на ядро. Указав большее значение, системе мы не навредим, но, скорее всего, остальные процессы просто будут простаивать.
Чтобы определить оптимальное значение worker_process просто посмотрите сколько ядер у вас в системе. Если вы используете настройку настройку DigitalOcean на 512 МБ, то скорее всего у вас одно ядро. При расширении системы, стоит посмотреть сколько у вас ядер и в соответствии с их количеством установить значение worker_process.
Следующая команда нам в этом поможет:
~~~{.bash} grep processor /proc/cpuinfo | wc -l
Допустим вы получили значение 1. Это и есть ответ на вопрос, сколько ядер в вашей системе. Переменная **worker_connections** определяет допустимое количество одновременных соединений обслуживаемых NGINX. Значение по умолчанию - 768, хотя, если принять во внимание, что один браузер как минимум открывает два соединения, то можно смело разделить это значение пополам. Поэтому и нужно задать максимально допустимое значение. Проверить ограничения ядра можно при помощи команды: ~~~{.bash} ulimit -nНа слабых машинах (512 МБ) это значение, скорее всего, будет 1024 - что и можно принять в качестве начального значения.
Обновим нашу настройку
~~~{.bash} sudo nano /etc/nginx/nginx.conf
~~~{.nginx} # /etc/nginx/nginx.conf worker_processes 1; worker_connections 1024;Помните, что максимальное количество обслуживаемых клиентов умножается на количество ядер в системе. В данном случае мы допускаем 1024 соединений в секунду. Так же это значение в последствии уменьшается директивой keepalive_timeout.
Буфер
Есть ещё небольшая уловка, которой мы можем воспользоваться. Это изменить размер буфера. Если этот размер слишком мал, то NGINX создаёт временный файл, тем самым провоцируя постоянные циклы чтения/записи с диска. Для начала, надо понять что означают следующие переменные.
client_body_buffer_size - размер буфера клиента. Это ограничения связанные с POST запросами. Обычно они используются при отправке форм.
client_header_buffer_size - то же самое, что и предыдущее, только ограничивает размер заголовков. Обычно 2К более чем достаточно.
client_max_body_size - максимальный размер запроса от клиента. При превышении этого ограничения NGINX выдаст ошибку 413 или Request Entity Too Large (размер запроса слишком велик)
large_client_header_buffers - максимальные число и размер буфера для больших заголовков.
~~~{.nginx}
client_body_buffer_size 10K; client_header_buffer_size 2k; client_max_body_size 8m; large_client_header_buffers 2 2k;
### Таймуаты Установка ограничений по времени также значительно увеличивает производительность. Переменные **client_body_timeout** и **client_header_timeout** отвечают за установку времени ожидания прежде чем ответить на запрос клиента, будь то основная часть или заголовок. Если же ни того, ни другого отправлено не было, то сервер генерирует ошибку 408 или Request Time Out (время ожидания запроса превышено). Переменная **keepalive_timeout** устанавливает время жизни соединения с клиентом. Проще говоря, сервер разрывает соединение с клиентом по истечению этого времени. И наконец, **send_timeout** устанавливается не на весь период передачи ответа, а только на промежуток между двумя операциями чтения. Если клиент по истечению этого времени не выполнял никаких действий, то сервер разрывает соединение с ним. ~~~{.nginx} # /etc/nginx/nginx.conf client_body_timeout 12; client_header_timeout 12; keepalive_timeout 15; send_timeout 10;
Gzip сжатие
Такое сжатие позволяет уменьшить размер сетевых данных, с которыми работает NGINX. Но будьте аккуратны, при достаточно большом значении, сервер начинает довольно сильно нагружать CPU.
~~~{.nginx}
gzip on; gzip_comp_level 2; gzip_min_length 1000; gzip_proxied expired no-cache no-store private auth; gzip_types text/plain application/x-javascript text/xml text/css application/xml;
Вы можете как добавлять, так и исключать типы файлов в массиве.
Ведение логов
NGINX отражает в своем логе каждый поступивший запрос. Если вы пользуетесь сторонними средствами мониторинга, то можете отключить эту функцию. Просто измените значение директивы access_log:
~~~{.nginx}
access_log off
Сохраните и закройте файл, а затем выполните: ~~~{.bash} sudo service nginx restartЗаключение
В конце концов, хорошо настроенный сервер это тот, который легко поддаётся мониторингу и тонкой настройке. Нельзя выделить уникальные настройки, каждый случай должен быть рассмотрен индивидуально. Более того, если уделить должное внимание быстродействию системы, то следует обратить внимание на балансировку и горизонтальное масштабирование системы. Это всего лишь несколько из многих пунктов по улучшению системы, которые должен знать каждый системный администратор .
devacademy.ru
Оптимизация связки Nginx, Apache, PHP, MySql / СоХабр
Неожиданно поступила задача разобраться почему определенный сайт не работает столь быстро сколь хочется. В основе его CakePHP, в связке с Apache и MySQL. В статье описание процесса поиска узких мест и приведение в порядок на столько, на сколько это возможно.Название сайта светить не буду — думаю, программисты сами узнают. Скажу лишь, что это приложение для социальной сети нагрузкой 70-150 тысяч посетителей в обычное время. Все усложняется тем, что периодически производится рекламная рассылка, которая привлекает около 200-300 тысяч посетителей за пару часов.
Итак, под катом описание всей борьбы на протяжении 4 дней. Данная статья нацелена на 2 группы людей. Первая — это конечно же мы, админы, которым приходится разгребать подобное безобразие. Иногда вытаскивая практически за уши умирающий сервер. Вторая часть аудитории, которые я надеюсь тоже обязательно прочтут весь материал — это программисты. Друзья, забегая вперед, скажу: не было бы такого безобразия, если бы вы правильно проектировали свои проекты, многих казусов можно было бы избежать. Особенно зная на какую аудиторию вы пишите свой проект.
В моем распоряжении оказался сервер EX10, находящийся на площадке hetzner. Для тех кто не в курсе — это 64 Гб ОЗУ и 6-ядерный процессор. Назовем это ядром системы. Есть еще 2 сервера, один со статикой, другой с бекэнд базой. Небольшое приложение, InnoDB база данных на 500Мб.
С постоянной нагрузкой, как позже выяснилось, 70-100 онлайн сессий, ситуация следующая: загрузка CPU по 100% на каждое ядро. В топе, понятное дело, MySQL и Apache дерутся за ресурсы системы.
Nginx
Первой попыткой было снизить нагрузку сервера за счет кеширования выдачи Apache с целью снять с него выдачу статики.Установил в очень простой конфигурации: он должен был все все файлы по маске пробовать забирать из определенной папки, если в ней файла нет — забирать его с проксируемого сервера, складывать в эту папку и выдавать клиенту.
http { proxy_cache_path /var/tmp/nginx_cache/ levels=1:2 keys_zone=ok:100m inactive=1d max_size=1024m; server { location ~* \.(js|JPG|jpg|png|jpeg|gif|zip|tgz|gz|rar|doc|xls|exe|pdf|ppt|txt|wav|bmp|rtf)$ { expires 1y; open_file_cache_errors off; error_page 404 = @fetch; root /var/tmp/_fetch_ok; } location @fetch { proxy_store_access user:rw group:rw all:r; proxy_store on; proxy_pass http://127.0.0.1:80; proxy_temp_path /var/tmp/_fetch_ok_temp; root /var/tmp/_fetch_ok; } location / { proxy_cache ok; proxy_pass http://127.0.0.1; proxy_cache_valid any 10m; proxy_buffer_size 8k; } } } Конфиг приведен не полностью, а только нужными для данного примера блоками.- Apache перестал выдавать картинки, то есть стал чуть менее загружен
- Apache перестал напрямую общаться с внешним миром, следовательно у него можно было выключить keep-alive и уменьшить количество детей.
- Нашлось первое узкое место — абсолютно всеми запросами на сайт управляет PHP скрипт на который .htaccess переадресует любой запрос. Включая, вы не поверите, всю статику, и даже css.
MySQL
Тут самое сложное, поскольку на данный момент у меня нет большого опыта в SQL запросах и оптимизации. Начал с чтения документации к MySQL.Так как имеем на руках InnoDB — я взял для исходной позиции конфигурационный файл из стандартной поставки my-innodb-heavy-4G.cnf
Ниже опишу параметры конфигурации, на которые следует обращать внимание на высоконагруженных проектах.
back_log = 5000max_connections = 1600 Первый параметр отвечает за количество соединений, которые могут находится в очереди до того момента, как сервер перестанет отвечать на новые запросы. Второй — сколько подключений может быть принято сервером. У меня эти значения достаточно большие, так как конкурентных сессий в среднем выходит до 1300. Ставить больше чем нужно — не стоит, так как каждое соединение может потребовать некоторого количества ОЗУ. Об этом позже.
max_connect_errors = 50 Тут просто — количество ошибок, которые может сделать клиент до того как получит дисконнект. Пришлось увеличить, в виду того что проект в стадии разработки и шансов некорректных запросов много.
table_cache = 2048 Открытие таблицы требует некоторых ресурсов, следовательно этот параметр отвечает за количество открытых таблиц ожидающих следующего соединения некоторое время после выполнения последнего. Узнать надо ли его менять можно по переменной SHOW GLOBAL STATUS LIKE 'Opened_tables'; Она не должна быть как можно меньше. Тут хорошо написано: http://www.mysql.ru/docs/man/Table_cache.html
max_allowed_packet = 16M Максимальный размер пакета. Если не пользуемся большими BLOB, изменять не имеет смысла.
binlog_cache_size = 1M Размер кеша бинарного лога, для транзакции. В официальной документации рекомендуют увеличивать если у нас большие транзакции. dev.mysql.com/doc/refman/5.5/en/replication-options-binary-log.html#sysvar_binlog_cache_size
max_heap_table_size = 64Mtmp_table_size = 64M Насколько я понимаю, учитывается меньший из них. Параметр отвечает за максимальный размер временной таблицы, умещающейся в памяти. Если таблица его достигает она кладется на диск. Следовательно необходимо стараться что бы таблиц на диске создавалось как можно меньше. Посмотреть какое отношение временных таблиц к таблицам на диске на данный момент можно запросив show status like '%tmp%tables';www.mysqlperformanceblog.com/2007/01/19/tmp_table_size-and-max_heap_table_size
sort_buffer_size = 8M Что бы не обмануть никого, не возьмусь переводить. Уточню лишь, что в документации советуют смотреть на этот параметр только если show status like '%Sort_merge_passes%'; больше нуляdev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_sort_buffer_size
join_buffer_size = 2M Насколько понимаю, максимальный размер буфера, рассчитанного на операции не использующие индекс. Пока не трогал.dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_join_buffer_size
thread_cache_size = 4096 Максимальное количество треадов, которые остаются для повторного использования после выполнения запроса. Полезно держать достаточным для того что бы MySQL как можно меньше делал новых треадов и использовал старые. Понять эффективность данного параметра можно по отношению параметров Threads_created / Connections; dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_thread_cache_size
query_cache_size = 256Mquery_cache_limit = 8M Я думаю тут лучше чем мой соратник, автор этого перевода habrahabr.ru/post/41166 никто не скажет. Это наверное наиболее важный параметр, так что лучше перечитать.
thread_stack = 192K Не возьмусь описывать назначение данного параметра, обращу лишь внимание на то что он тоже влияет на количество потребляемой ОЗУ, так как тоже выделяется на каждое соединение. Следовательно опять умножайте на max connections
long_query_time = 2log_long_formatlog-queries-not-using-indexes У сервера MySQL есть очень удобный инструмент для оценки производительности БД. Это лог файл длинных запросов. По моему опыту это чаще всего неэффективные запросы или запросы неиспользующие индекс. Советую с этим лог файлом идти к программерам.
key_buffer_size = 1G Параметр отвечает за кеширование индексов в памяти, для оптимизации данного значения смотрите на Key_read_requests, Key_reads. Второй параметр отвечает за количество чтений с диска а не их буфера.mysqltips.blogspot.com/2007/03/key-buffer.html
read_buffer_size = 1Mboombick.org/blog/posts/3 — После прочтения этого текста, не рискну что либо добавлять, так как не буду уверен в своей правоте.
read_rnd_buffer_size = 24M Параметр влияет на скорость операций сортировки. К сожалению не нашел как оценить его эффективность.dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_read_rnd_buffer_sizewww.mysqlperformanceblog.com/2007/07/24/what-exactly-is-read_rnd_buffer_size
myisam_sort_buffer_size = 128Mmyisam_max_sort_file_size = 10Gmyisam_max_extra_sort_file_size = 10G Параметры влияют на сортировку, так же не рискнул их менять. Увеличил первый предполагая, что это увеличит производительность сложных запросов.
sync_binlog = 0 В нашем случае означает не синхронизировать бинарных лог на диск через системные функции. Если параметр больше нуля, то сервер будет синхронизировать данные каждые n запросов. dev.mysql.com/doc/refman/5.5/en/replication-options-binary-log.html#sysvar_sync_binlog
innodb_buffer_pool_size = 4G Увеличение этого параметра снижает количество дисковых операций. К сожалению тоже не нашел как его лучше замерить. Поскольку база небольшая решил его сильно не увеличивать. Где-то встречал совет, в случае большой БД увеличивать этот параметр до 70% ОЗУ.dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_buffer_pool_size
innodb_log_buffer_size = 32M Если верить описанию, снижает дисковые операции при тяжелых транзакциях.dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_log_buffer_size
innodb_log_file_size = 1024M Если верить документации, то увеличение лог файла уменьшает загруженность IO операций диска, но увеличивает время восстановления в случае сбоев.dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_log_file_size
innodb_flush_log_at_trx_commit = 0 В значении 0, сброс буферов происходит раз в минутусекунду, а не после каждого инсерта.dev.mysql.com/doc/refman/5.1/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit
innodb_thread_concurrency = 14 Рекомендуют ставить чуть больше чем количество ядер.
innodb_sync_spin_loops=10 Насколько я понял, влияет на количество попыток доступа к заблокированным данным. Увеличивая данное значение мы можем потерять процессорное время, а уменьшая — надежность записи в БД.www.mysqlperformanceblog.com/2006/09/07/internals-of-innodb-mutexesdev.mysql.com/doc/refman/5.0/en/innodb-parameters.html#sysvar_innodb_sync_spin_loops
БД и оперативная память
Есть прекрасный скрипт на perl который дает базовое понимание того что надо поменять в БД. mysqltuner.pl/mysqltuner.pl Очень часто данный скрипт ругается на максимальное потребление памяти сервером mysql. Если посмотреть в исходник — вот как эта программа считает использование памяти:per_thread_buffers = read_buffer_size +read_rnd_buffer_size + sort_buffer_size + thread_stack +join_buffer_size; total_per_thread_buffers = per_thread_buffers * max_connections; server_buffers = key_buffer_size +max_tmp_table_size; server_buffers +=innodb_buffer_pool_size; server_buffers +=innodb_additional_mem_pool_size; server_buffers +=innodb_log_buffer_size; server_buffers +=query_cache_size; total_possible_used_memory =server_buffers + total_per_thread_buffers;
Мне было полезно понять где я не правильно указал значения. Кстати, сразу кидаться занижать параметры, если скрипт ругается на большое потребление ОЗУ базой, не стоит. Так как многие отмечают, что это лишь теоретический показатель и БД может никогда не попытаться забрать себе столько памяти.
Оптимизация структуры БД.
Когда мы сделали все возможные настройки и все равно не получили хорошего результата — пора обратиться к slow-log файлу. В стандартной комплектации к mysql есть приложение mysqldumpslow. Запустив mysqldumpslow -s c отсортированный по количеству вхождений список запросов к базе данных, которые были слишком долгими или не использовали индексы. Обычно добавление правильных индексов исправляет обе проблемы.В большинстве случаев, когда вы видите в выводе этой программы большое количество долгих запросов(переменная count), копируете кусок этого запроса и ищете в тексте лог файла пример такого запроса. Далее заходите в клиент БД и выполняете этот запрос, добавив вначале слово explain. Про это так же можно прочитать подробнее вот тут:habrahabr.ru/post/31072
Так можно увидеть использует ли запрос индекс или нет. Если таблице не хватает индексов их можно смело добавлять, хотя перебарщивать тоже не стоит. Индексы нужны тем столбцам которые используются после where и order. Начинайте с уникальных индексов на каждый стоблец. Иначе индекс может не сработать. Вот тут можно узнать более подробно как эти индексы работают: dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html
Скажу честно, после оптимизации примерно 5 ключевых запросов сервер смог обрабатывать 500-700 подключений вместо 50, а время выдачи php-страницы сократилось до 1с вместо 8с. При максимальной нагрузке время выдачи страницы составило 5с вместо 50с. (Имеются в виду замеры производительности с помощью Apache Benchmark c примерно 1000 потоков)
Еще немного об nginx.
После оптимизации заметил, что при больших нагрузках откидывает запросы больше определенного количества уже сам nginx, а не apache. При этом память и CPU не загружены. Стал разбираться. Увидел в логах, что сервер nginx пытается открыть файлов больше, чем ему положено. В ОС Suse, с которым мне пришлось столкнуться, за это ограничение отвечает файл /etc/security/limits.conf Дописал я туда вот такие строчки: nginx soft nofile 300000 nginx hard nofile 300000 Рестарта сервера не понадобилось.Apache2
Сильно изменять конфигурацию я пока не стал. Единственное что сделал — так это выключил keep-alive. Что бы апач мог спокойно выдать ответ и заняться следующим запросом в тот момент, когда nginx все еще отдает клиенту страницу по медленному каналу.eAccelerator
Не забываем о том что у данного оптимизатора тоже есть некоторое количество параметров которые можно поменять.Вот что изменил я:eaccelerator.shm_size = «2096» Размер виртуальной памяти в мегабайтах который можно использовать
eaccelerator.shm_only = «1» — использовать только ОЗУ и не использовать диск, в борьбе за io на софт рейде из 2 сата дисков решил сделать так.
Вот еще что будет полезно почитать:
habrahabr.ru/post/41166habrahabr.ru/post/108418dev.mysql.com/doc/refman/5.5/en/server-system-variables.htmlОтдельное спасибо моему другу, который помог вычитать статью и исправить целую гору грамматических и стилистических ошибок.
Вместо послесловия
Друзья, я понимаю что ошибок и заблуждений в выводах приведенных в этой статье целая куча. Большая просьба к гуру, своими комментариями, помочь сделать статью лучше.UPD. Резюме по комментам
Спасибо за столь большой интерес. Не ожидал.Хорошая ссылка, рекомендую к прочтению: www.percona.com/files/presentations/percona-live/dc-2012/PLDC2012-optimizing-mysql-configuration.pdf Спасибо Albertum
По поводу систем кеширования php: да действительно eAccelerator не единственный вариант. Есть так же APC, и да действительно его собираются встроить в PHP. Что лучше судить не возьмусь, так как не делал достаточного количества тестов.
Альтернативы MySQL тоже присутствуют и их много. Ключевые конечно: mAriadb percona
Сам я лично выбрал перкону и пока доволен.
По поводу того что это не конечный светлый результат и надо двигаться дальше — те кто внимательно читал увидит что это лишь латание дыр.
sohabr.net
Оптимизация nginx | Nulled Warez Scripts
ask0n сказал(а): ↑...
Нажмите, чтобы раскрыть...
На сервере 1,7 Гб оперативы.Конфиг апача:ServerTokens OS ServerRoot "/etc/httpd" PidFile run/httpd.pid Timeout 120 KeepAlive Off MaxKeepAliveRequests 100 KeepAliveTimeout 15 <IfModule worker.c> StartServers 2 MaxClients 150 MinSpareThreads 25 MaxSpareThreads 75 ThreadsPerChild 25 MaxRequestsPerChild 0 </IfModule>
LoadModule auth_basic_module modules/mod_auth_basic.so LoadModule auth_digest_module modules/mod_auth_digest.so LoadModule authn_file_module modules/mod_authn_file.so LoadModule authn_alias_module modules/mod_authn_alias.so LoadModule authn_anon_module modules/mod_authn_anon.so LoadModule authn_dbm_module modules/mod_authn_dbm.so LoadModule authn_default_module modules/mod_authn_default.so LoadModule authz_host_module modules/mod_authz_host.so LoadModule authz_user_module modules/mod_authz_user.so LoadModule authz_owner_module modules/mod_authz_owner.so LoadModule authz_groupfile_module modules/mod_authz_groupfile.so LoadModule authz_dbm_module modules/mod_authz_dbm.so LoadModule authz_default_module modules/mod_authz_default.so LoadModule ldap_module modules/mod_ldap.so LoadModule authnz_ldap_module modules/mod_authnz_ldap.so LoadModule include_module modules/mod_include.so LoadModule log_config_module modules/mod_log_config.so LoadModule logio_module modules/mod_logio.so LoadModule env_module modules/mod_env.so LoadModule ext_filter_module modules/mod_ext_filter.so LoadModule mime_magic_module modules/mod_mime_magic.so LoadModule expires_module modules/mod_expires.so LoadModule deflate_module modules/mod_deflate.so LoadModule headers_module modules/mod_headers.so LoadModule usertrack_module modules/mod_usertrack.so LoadModule setenvif_module modules/mod_setenvif.so LoadModule mime_module modules/mod_mime.so LoadModule dav_module modules/mod_dav.so LoadModule status_module modules/mod_status.so LoadModule autoindex_module modules/mod_autoindex.so LoadModule info_module modules/mod_info.so LoadModule dav_fs_module modules/mod_dav_fs.so LoadModule vhost_alias_module modules/mod_vhost_alias.so LoadModule dir_module modules/mod_dir.so LoadModule actions_module modules/mod_actions.so LoadModule speling_module modules/mod_speling.so LoadModule userdir_module modules/mod_userdir.so LoadModule alias_module modules/mod_alias.so LoadModule rewrite_module modules/mod_rewrite.so LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_balancer_module modules/mod_proxy_balancer.so LoadModule proxy_ftp_module modules/mod_proxy_ftp.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_connect_module modules/mod_proxy_connect.so LoadModule suexec_module modules/mod_suexec.so LoadModule cgi_module modules/mod_cgi.so
Include conf.d/*.conf
User bitrix Group bitrix
ServerAdmin root@localhost
UseCanonicalName Off
DocumentRoot "/home/bitrix/www"
<Directory /> Options FollowSymLinks AllowOverride None </Directory>
<IfModule mod_userdir.c> UserDir disable </IfModule>
DirectoryIndex index.html index.html.var
AccessFileName .htaccess
<Files ~ "^\.ht"> Order allow,deny Deny from all </Files>
TypesConfig /etc/mime.types DefaultType text/plain <IfModule mod_mime_magic.c> MIMEMagicFile conf/magic </IfModule>
HostnameLookups Off
ErrorLog logs/error_log
LogLevel warn LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined LogFormat "%h %l %u %t \"%r\" %>s %b" common LogFormat "%{Referer}i -> %U" referer LogFormat "%{User-agent}i" agent
CustomLog logs/access_log combined
ServerSignature On
<IfModule mod_dav_fs.c> # Location of the WebDAV lock database. DAVLockDB /var/lib/dav/lockdb </IfModule>
IndexOptions FancyIndexing VersionSort NameWidth=* HTMLTable
IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t
AddLanguage ca .ca AddLanguage cs .cz .cs AddLanguage da .dk AddLanguage de .de AddLanguage el .el AddLanguage en .en AddLanguage eo .eo AddLanguage es .es AddLanguage et .et AddLanguage fr .fr AddLanguage he .he AddLanguage hr .hr AddLanguage it .it AddLanguage ja .ja AddLanguage ko .ko AddLanguage ltz .ltz AddLanguage nl .nl AddLanguage nn .nn AddLanguage no .no AddLanguage pl .po AddLanguage pt .pt AddLanguage pt-BR .pt-br AddLanguage ru .ru AddLanguage sv .sv AddLanguage zh-CN .zh-cn AddLanguage zh-TW .zh-tw
AddDefaultCharset UTF-8
AddType application/x-compress .Z AddType application/x-gzip .gz .tgz
AddHandler type-map var AddType text/html .shtml AddOutputFilter INCLUDES .shtml
BrowserMatch "Mozilla/2" nokeepalive BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 BrowserMatch "RealPlayer 4\.0" force-response-1.0 BrowserMatch "Java/1\.0" force-response-1.0 BrowserMatch "JDK/1\.0" force-response-1.0 BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully BrowserMatch "MS FrontPage" redirect-carefully BrowserMatch "^WebDrive" redirect-carefully BrowserMatch "^WebDAVFS/1.[0123]" redirect-carefully BrowserMatch "^gnome-vfs/1.0" redirect-carefully BrowserMatch "^XML Spy" redirect-carefully BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully Конфиг nginx:user bitrix; worker_processes 8; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; worker_rlimit_nofile 10240;
events { use epoll; worker_connections 10240;
}
http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] $status ' '"$request" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; log_format common '$remote_addr - - [$time_local] "$request" $status $bytes_sent "$http_referer" "$http_user_agent" $msec';
access_log /var/log/nginx/access.log common; sendfile on; tcp_nopush on; tcp_nodelay on; client_max_body_size 10m; client_body_buffer_size 128k; proxy_connect_timeout 300; proxy_send_timeout 300; proxy_read_timeout 300; proxy_buffer_size 64k; proxy_buffers 8 64k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 10m; gzip on; gzip_proxied any; gzip_types application/x-javascript text/css;
server { listen 80; server_name bitrix; server_name_in_redirect off; access_log /var/log/nginx/access.log common; index index.php; error_page 500 502 503 504 /500.html; error_page 404 = /404.php; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host:80; client_max_body_size 1024M; client_body_buffer_size 4M; root /home/bitrix/www;
if (-f /home/bitrix/www/.htsecure) { rewrite ^(.*)$ https://$host$1 permanent; }
location / { expires 3d; if ($request_method = OPTIONS ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = PROPFIND ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = PROPPATCH ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = MKCOL ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = COPY ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = MOVE ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = LOCK ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = UNLOCK ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = PUT ) { proxy_pass http://127.0.0.1:8888; } }
location ~ ^(/extranet/docs|/docs|/workgroups|/company/profile|/bitrix/tools|/company/personal/user).*/$ { proxy_pass http://127.0.0.1:8888; }
location ~ ^(/extranet/docs|/docs|/workgroups|/company/profile|/bitrix/tools|/company/personal/user) { if (-d $request_filename) { rewrite ^(.*)(/*)$ $1/ last; } proxy_pass http://127.0.0.1:8888; }
location ~ ^(/bitrix/html_pages) { root /home/bitrix/www; index [email protected]; if (!-f $request_filename) { rewrite ^/bitrix/html_pages(.*)@(.*)\.html$ $1.php?$2 break; rewrite ^/bitrix/html_pages(.*)\.html$ $1\.php break; proxy_pass http://127.0.0.1:8888; } }
location ~ \.php$ { root /home/bitrix/www; if ($request_method = POST ) { break; proxy_pass http://127.0.0.1:8888; } if ($http_cookie !~ "PHPSESSID=" ) { rewrite ^(.*)\.php$ /bitrix/html_pages$1@$args.html? last; } proxy_pass http://127.0.0.1:8888; }
location ~ /$ { root /home/bitrix/www; if ($request_method = POST ) { break; proxy_pass http://127.0.0.1:8888; } if ($http_cookie !~ "PHPSESSID=" ) { rewrite ^(.*)/$ /bitrix/html_pages$1/index@$args.html? last; } proxy_pass http://127.0.0.1:8888; }
location ~ (/|\.php|\.asmx)$ { proxy_pass http://127.0.0.1:8888; } location ~ /\.ht { deny all; } location ~ /favicon.ico { proxy_pass http://127.0.0.1:8888; }
location ~ ^(/bitrixsetup\.php)$ { proxy_pass http://127.0.0.1:8888; proxy_buffering off; }
}
server { listen 443; keepalive_timeout 70; #-> keepalive_requests 150; #<- server_name bitrix;
ssl on; # ssl_protocols SSLv3 TLSv1; # ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHAES-CBC3-SHA:RC4-MD5; ssl_certificate /etc/nginx/ssl/cert.pem; ssl_certificate_key /etc/nginx/ssl/cert.pem; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m;
server_name_in_redirect off; access_log /var/log/nginx/access.log main; index index.php; error_page 500 502 503 504 /500.html; error_page 404 = /404.php; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host:443; client_max_body_size 1024M; client_body_buffer_size 4M; proxy_set_header HTTPS YES; root /home/bitrix/www;
location / { expires 3d; if ($request_method = OPTIONS ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = PROPFIND ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = PROPPATCH ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = MKCOL ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = COPY ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = MOVE ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = LOCK ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = UNLOCK ) { proxy_pass http://127.0.0.1:8888; } if ($request_method = PUT ) { proxy_pass http://127.0.0.1:8888; } }
location ~ ^(/extranet/docs|/docs|/workgroups|/company/profile|/bitrix/tools|/company/personal/user).*/$ { proxy_pass http://127.0.0.1:8888; }
location ~ ^(/extranet/docs|/docs|/workgroups|/company/profile|/bitrix/tools|/company/personal/user) { if (-d $request_filename) { rewrite ^(.*)(/*)$ $1/ last; } proxy_pass http://127.0.0.1:8888; }
location ~ ^(/bitrix/html_pages) { root /home/bitrix/www; index [email protected]; if (!-f $request_filename) { rewrite ^/bitrix/html_pages(.*)@(.*)\.html$ $1.php?$2 break; rewrite ^/bitrix/html_pages(.*)\.html$ $1\.php break; proxy_pass http://127.0.0.1:8888; } }
location ~ \.php$ { root /home/bitrix/www; if ($request_method = POST ) { break; proxy_pass http://127.0.0.1:8888; } if ($http_cookie !~ "PHPSESSID=" ) { rewrite ^(.*)\.php$ /bitrix/html_pages$1@$args.html? last; } proxy_pass http://127.0.0.1:8888; }
location ~ /$ { root /home/bitrix/www; if ($request_method = POST ) { break; proxy_pass http://127.0.0.1:8888; } if ($http_cookie !~ "PHPSESSID=" ) { rewrite ^(.*)/$ /bitrix/html_pages$1/index@$args.html? last; } proxy_pass http://127.0.0.1:8888; }
location ~ (/|\.php|\.asmx)$ { proxy_pass http://127.0.0.1:8888; } location ~ /\.ht { deny all; } location ~ /favicon.ico { proxy_pass http://127.0.0.1:8888; }
location ~ ^(/bitrixsetup\.php)$ { proxy_pass http://127.0.0.1:8888; proxy_buffering off; }
} }
nulled.in
Оптимизация связки Nginx, Apache, PHP, MySql
Неожиданно поступила задача разобраться почему определенный сайт не работает столь быстро сколь хочется. В основе его CakePHP, в связке с Apache и MySQL. В статье описание процесса поиска узких мест и приведение в порядок на столько, на сколько это возможно.Название сайта светить не буду — думаю, программисты сами узнают. Скажу лишь, что это приложение для социальной сети нагрузкой 70-150 тысяч посетителей в обычное время. Все усложняется тем, что периодически производится рекламная рассылка, которая привлекает около 200-300 тысяч посетителей за пару часов.Итак, под катом описание всей борьбы на протяжении 4 дней.
Данная статья нацелена на 2 группы людей. Первая — это конечно же мы, админы, которым приходится разгребать подобное безобразие. Иногда вытаскивая практически за уши умирающий сервер. Вторая часть аудитории, которые я надеюсь тоже обязательно прочтут весь материал — это программисты. Друзья, забегая вперед, скажу: не было бы такого безобразия, если бы вы правильно проектировали свои проекты, многих казусов можно было бы избежать. Особенно зная на какую аудиторию вы пишите свой проект.
В моем распоряжении оказался сервер EX10, находящийся на площадке hetzner. Для тех кто не в курсе — это 64 Гб ОЗУ и 6-ядерный процессор. Назовем это ядром системы. Есть еще 2 сервера, один со статикой, другой с бекэнд базой. Небольшое приложение, InnoDB база данных на 500Мб.
С постоянной нагрузкой, как позже выяснилось, 70-100 онлайн сессий, ситуация следующая: загрузка CPU по 100% на каждое ядро. В топе, понятное дело, MySQL и Apache дерутся за ресурсы системы.
Nginx
Первой попыткой было снизить нагрузку сервера за счет кеширования выдачи Apache с целью снять с него выдачу статики.
Установил в очень простой конфигурации: он должен был все все файлы по маске пробовать забирать из определенной папки, если в ней файла нет — забирать его с проксируемого сервера, складывать в эту папку и выдавать клиенту.
http { proxy_cache_path /var/tmp/nginx_cache/ levels=1:2 keys_zone=ok:100m inactive=1d max_size=1024m; server { location ~* \.(js|JPG|jpg|png|jpeg|gif|zip|tgz|gz|rar|doc|xls|exe|pdf|ppt|txt|wav|bmp|rtf)$ { expires 1y; open_file_cache_errors off; error_page 404 = @fetch; root /var/tmp/_fetch_ok; } location @fetch { proxy_store_access user:rw group:rw all:r; proxy_store on; proxy_pass http://127.0.0.1:80; proxy_temp_path /var/tmp/_fetch_ok_temp; root /var/tmp/_fetch_ok; } location / { proxy_cache ok; proxy_pass http://127.0.0.1; proxy_cache_valid any 10m; proxy_buffer_size 8k; } } }Конфиг приведен не полностью, а только нужными для данного примера блоками.
К сожалению, не очень это привело к каким-то результатам.Плюсов было всего 3:
- Apache перестал выдавать картинки, то есть стал чуть менее загружен
- Apache перестал напрямую общаться с внешним миром, следовательно у него можно было выключить keep-alive и уменьшить количество детей.
- Нашлось первое узкое место — абсолютно всеми запросами на сайт управляет PHP скрипт на который .htaccess переадресует любой запрос. Включая, вы не поверите, всю статику, и даже css.
MySQL
Тут самое сложное, поскольку на данный момент у меня нет большого опыта в SQL запросах и оптимизации.Начал с чтения документации к MySQL.
Так как имеем на руках InnoDB — я взял для исходной позиции конфигурационный файл из стандартной поставки my-innodb-heavy-4G.cnf
Ниже опишу параметры конфигурации, на которые следует обращать внимание на высоконагруженных проектах.
back_log = 5000max_connections = 1600Первый параметр отвечает за количество соединений, которые могут находится в очереди до того момента, как сервер перестанет отвечать на новые запросы. Второй — сколько подключений может быть принято сервером.У меня эти значения достаточно большие, так как конкурентных сессий в среднем выходит до 1300. Ставить больше чем нужно — не стоит, так как каждое соединение может потребовать некоторого количества ОЗУ. Об этом позже.
max_connect_errors = 50Тут просто — количество ошибок, которые может сделать клиент до того как получит дисконнект. Пришлось увеличить, в виду того что проект в стадии разработки и шансов некорректных запросов много.
table_cache = 2048Открытие таблицы требует некоторых ресурсов, следовательно этот параметр отвечает за количество открытых таблиц ожидающих следующего соединения некоторое время после выполнения последнего.Узнать надо ли его менять можно по переменнойSHOW GLOBAL STATUS LIKE 'Opened_tables'; Она не должна быть как можно меньше.Тут хорошо написано: http://www.mysql.ru/docs/man/Table_cache.html
max_allowed_packet = 16MМаксимальный размер пакета. Если не пользуемся большими BLOB, изменять не имеет смысла.
binlog_cache_size = 1MРазмер кеша бинарного лога, для транзакции. В официальной документации рекомендуют увеличивать если у нас большие транзакции.dev.mysql.com/doc/refman/5.5/en/replication-options-binary-log.html#sysvar_binlog_cache_size
max_heap_table_size = 64Mtmp_table_size = 64MНасколько я понимаю, учитывается меньший из них. Параметр отвечает за максимальный размер временной таблицы, умещающейся в памяти. Если таблица его достигает она кладется на диск. Следовательно необходимо стараться что бы таблиц на диске создавалось как можно меньше. Посмотреть какое отношение временных таблиц к таблицам на диске на данный момент можно запросивshow status like '%tmp%tables';www.mysqlperformanceblog.com/2007/01/19/tmp_table_size-and-max_heap_table_size/
sort_buffer_size = 8MЧто бы не обмануть никого, не возьмусь переводить. Уточню лишь, что в документации советуют смотреть на этот параметр только еслиshow status like ‘%Sort_merge_passes%’; больше нуляdev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_sort_buffer_size
join_buffer_size = 2MНасколько понимаю, максимальный размер буфера, рассчитанного на операции не использующие индекс. Пока не трогал.dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_join_buffer_size
thread_cache_size = 4096Максимальное количество треадов, которые остаются для повторного использования после выполнения запроса. Полезно держать достаточным для того что бы MySQL как можно меньше делал новых треадов и использовал старые. Понять эффективность данного параметра можно по отношению параметров Threads_created / Connections;dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_thread_cache_size
query_cache_size = 256Mquery_cache_limit = 8MЯ думаю тут лучше чем мой соратник, автор этого перевода habrahabr.ru/post/41166/ никто не скажет.Это наверное наиболее важный параметр, так что лучше перечитать.
thread_stack = 192KНе возьмусь описывать назначение данного параметра, обращу лишь внимание на то что он тоже влияет на количество потребляемой ОЗУ, так как тоже выделяется на каждое соединение. Следовательно опять умножайте на max connections
long_query_time = 2log_long_formatlog-queries-not-using-indexesУ сервера MySQL есть очень удобный инструмент для оценки производительности БД. Это лог файл длинных запросов. По моему опыту это чаще всего неэффективные запросы или запросы неиспользующие индекс.Советую с этим лог файлом идти к программерам.
key_buffer_size = 1GПараметр отвечает за кеширование индексов в памяти, для оптимизации данного значения смотрите на Key_read_requests, Key_reads. Второй параметр отвечает за количество чтений с диска а не их буфера.mysqltips.blogspot.com/2007/03/key-buffer.html
read_buffer_size = 1Mboombick.org/blog/posts/3 — После прочтения этого текста, не рискну что либо добавлять, так как не буду уверен в своей правоте.
read_rnd_buffer_size = 24MПараметр влияет на скорость операций сортировки. К сожалению не нашел как оценить его эффективность.dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_read_rnd_buffer_sizewww.mysqlperformanceblog.com/2007/07/24/what-exactly-is-read_rnd_buffer_size/
myisam_sort_buffer_size = 128Mmyisam_max_sort_file_size = 10Gmyisam_max_extra_sort_file_size = 10GПараметры влияют на сортировку, так же не рискнул их менять. Увеличил первый предполагая, что это увеличит производительность сложных запросов.
sync_binlog = 0В нашем случае означает не синхронизировать бинарных лог на диск через системные функции. Если параметр больше нуля, то сервер будет синхронизировать данные каждые n запросов.dev.mysql.com/doc/refman/5.5/en/replication-options-binary-log.html#sysvar_sync_binlog
innodb_buffer_pool_size = 4GУвеличение этого параметра снижает количество дисковых операций. К сожалению тоже не нашел как его лучше замерить. Поскольку база небольшая решил его сильно не увеличивать. Где-то встречал совет, в случае большой БД увеличивать этот параметр до 70% ОЗУ.dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_buffer_pool_size
innodb_log_buffer_size = 32MЕсли верить описанию, снижает дисковые операции при тяжелых транзакциях.dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_log_buffer_size
innodb_log_file_size = 1024MЕсли верить документации, то увеличение лог файла уменьшает загруженность IO операций диска, но увеличивает время восстановления в случае сбоев.dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_log_file_size
innodb_flush_log_at_trx_commit = 0В значении 0, сброс буферов происходит раз в минутусекунду, а не после каждого инсерта.dev.mysql.com/doc/refman/5.1/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit
innodb_thread_concurrency = 14Рекомендуют ставить чуть больше чем количество ядер.
innodb_sync_spin_loops=10Насколько я понял, влияет на количество попыток доступа к заблокированным данным. Увеличивая данное значение мы можем потерять процессорное время, а уменьшая — надежность записи в БД.www.mysqlperformanceblog.com/2006/09/07/internals-of-innodb-mutexes/dev.mysql.com/doc/refman/5.0/en/innodb-parameters.html#sysvar_innodb_sync_spin_loops
БД и оперативная память
Есть прекрасный скрипт на perl который дает базовое понимание того что надо поменять в БД. mysqltuner.pl/mysqltuner.plОчень часто данный скрипт ругается на максимальное потребление памяти сервером mysql.Если посмотреть в исходник — вот как эта программа считает использование памяти:
per_thread_buffers = read_buffer_size +read_rnd_buffer_size + sort_buffer_size + thread_stack +join_buffer_size;total_per_thread_buffers = per_thread_buffers * max_connections;server_buffers = key_buffer_size +max_tmp_table_size;server_buffers +=innodb_buffer_pool_size;server_buffers +=innodb_additional_mem_pool_size;server_buffers +=innodb_log_buffer_size;server_buffers +=query_cache_size;total_possible_used_memory =server_buffers + total_per_thread_buffers;
Мне было полезно понять где я не правильно указал значения.Кстати, сразу кидаться занижать параметры, если скрипт ругается на большое потребление ОЗУ базой, не стоит. Так как многие отмечают, что это лишь теоретический показатель и БД может никогда не попытаться забрать себе столько памяти.
Оптимизация структуры БД.
Когда мы сделали все возможные настройки и все равно не получили хорошего результата — пора обратиться к slow-log файлу.В стандартной комплектации к mysql есть приложение mysqldumpslow.Запустивmysqldumpslow -s c <путь к слоу-лог файлу>отсортированный по количеству вхождений список запросов к базе данных, которые были слишком долгими или не использовали индексы. Обычно добавление правильных индексов исправляет обе проблемы.
В большинстве случаев, когда вы видите в выводе этой программы большое количество долгих запросов(переменная count), копируете кусок этого запроса и ищете в тексте лог файла пример такого запроса.Далее заходите в клиент БД и выполняете этот запрос, добавив вначале слово explain.Про это так же можно прочитать подробнее вот тут:habrahabr.ru/post/31072/
Так можно увидеть использует ли запрос индекс или нет.Если таблице не хватает индексов их можно смело добавлять, хотя перебарщивать тоже не стоит. Индексы нужны тем столбцам которые используются после where и order. Начинайте с уникальных индексов на каждый стоблец. Иначе индекс может не сработать.Вот тут можно узнать более подробно как эти индексы работают:dev.mysql.com/doc/refman/5.0/en/mysql-indexes.html
Скажу честно, после оптимизации примерно 5 ключевых запросов сервер смог обрабатывать 500-700 подключений вместо 50, а время выдачи php-страницы сократилось до 1с вместо 8с. При максимальной нагрузке время выдачи страницы составило 5с вместо 50с. (Имеются в виду замеры производительности с помощью Apache Benchmark c примерно 1000 потоков)
Еще немного об nginx.
После оптимизации заметил, что при больших нагрузках откидывает запросы больше определенного количества уже сам nginx, а не apache. При этом память и CPU не загружены.Стал разбираться. Увидел в логах, что сервер nginx пытается открыть файлов больше, чем ему положено.В ОС Suse, с которым мне пришлось столкнуться, за это ограничение отвечает файл/etc/security/limits.confДописал я туда вот такие строчки:
nginx soft nofile 300000 nginx hard nofile 300000Рестарта сервера не понадобилось.
Apache2
Сильно изменять конфигурацию я пока не стал. Единственное что сделал — так это выключил keep-alive. Что бы апач мог спокойно выдать ответ и заняться следующим запросом в тот момент, когда nginx все еще отдает клиенту страницу по медленному каналу.
eAccelerator
Не забываем о том что у данного оптимизатора тоже есть некоторое количество параметров которые можно поменять.
Вот что изменил я:eaccelerator.shm_size = «2096»Размер виртуальной памяти в мегабайтах который можно использовать
eaccelerator.shm_only = «1» — использовать только ОЗУ и не использовать диск, в борьбе за io на софт рейде из 2 сата дисков решил сделать так.
Вот еще что будет полезно почитать:
habrahabr.ru/post/41166/habrahabr.ru/post/108418/dev.mysql.com/doc/refman/5.5/en/server-system-variables.html
Отдельное спасибо моему другу, который помог вычитать статью и исправить целую гору грамматических и стилистических ошибок.
Вместо послесловия
Друзья, я понимаю что ошибок и заблуждений в выводах приведенных в этой статье целая куча.Большая просьба к гуру, своими комментариями, помочь сделать статью лучше.
UPD, Резюме по комментам
Спасибо за столь большой интерес. Не ожидал.
хорошая ссылка, рекомендую к прочтениюwww.percona.com/files/presentations/percona-live/dc-2012/PLDC2012-optimizing-mysql-configuration.pdfспасибо Albertum
По поводу систем кеширования php:да действительно eAccelerator не единственный вариант.Есть так же APC, и да действительно его собираются встроить в PHP.Что лучше судить не возьмусь, так как не делал достаточного количества тестов.
Альтернативы MySQL тоже присутствуют и их много.Ключевые конечно:mAriadbpercona
Сам я лично выбрал перкону и пока доволен.
По поводу того что это не конечный светлый результат и надо двигаться дальше — те кто внимательно читал увидит что это лишь латание дыр.
csslike.me
Оптимизация nginx / СоХабр
Как известно, nginx очень продуктивный веб-сервер, но всё же его тоже можно ускорить. Я облазил интернет и решил все советы собрать в одном месте. Поехали. Для начала нужно открыть конфиг nginx, ну, это и так должно бить понято. В debian он обычно в /etc/nginx/nginx.conf, в freebsd — /usr/local/etc/nginx/nginx.conf
Оптимизация
Я уже, наверное, задолбал теорией, так что давайте приступим к практике.Обработка соединений
Максимальное количество соединений, которые Nginx может обслуживать одновременно, определяются произведением двух параметров:Всего соединений = worker_processes x worker_connections worker_processes auto;Nginx и сам может посчитать и поверьте, сделает это лучше. Рекомендую ставить в auto в последних версиях.
worker_connections 1024;Устанавливает максимальное количество соединений одного рабочего процесса. Следует выбирать значения от 1024 до 4096.
А use нам поможет выбрать тип соединения. В linux — epoll, freebsd — kqueue
use epoll;Если не укажем — nginx сам подумает. Умница! А вы говорите нет штучного интелекта.
Обработка запросов
multi_accept on; #nginx будет обрабатывать максимальное количество соединений sendfile on; #Метод отправки данных sendfile более эффективен, чем стандартный метод read+write # Nginx будет отправлять заголовки и начало файла в одном пакете: tcp_nodelay on; tcp_nopush on;Без этого никак.
Файлы
directio 10m;Эта опция будет читать файлы, больше 10 мб, минуя операционный кеш, поскольку для больших файлов он малоэффективен.
Cache-Control
location ~* ^.+\.(js|css)$ { expires max; }Этот код будет кешировать js, css -файлы навечно. Но по скольку они время от времени изменяются, нужно делать так:
<link rel="stylesheet" href="/styles.v2.css" type="text/css" /> <script src="/scripts.v2.js"></script>Для удобства сделать rewrite на script.js/style.css и заливать файлы так, а если есть изменения — просто заменить цифру после v. С неизменяемой статикой всё значительно проще:
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ { expires max; } UPD: товарищ symbix советует указывать не regexp, а папку где оно хранится:location /static/ {expires max;}Информация о файлах
Nginx умеет кешировать информацию о файлах, с которыми ему приходится работать (например, css стили или картинки). Если к таким файлам происходит много обращений, кеширование может значительно ускорить этот процесс.open_file_cache max=200000 inactive=20s;Определяет максимальное количество файлов, информация о которых будет содержаться в кеше.
open_file_cache_valid 30s;Определяет через какое время информация будет удалена из кеша:
open_file_cache_min_uses 2;Nginx будет кешировать информацию о файлах, которые были использованы хотя бы 2 раза.
open_file_cache_errors on;Nginx будет кешировать информацию об отсутствующих файлах:
limit_rate
limit_rate 200K; #отдавать файлы со скоростью 200 кб/секЭта опция работает только в рамках одного запроса, а не клиента. Если вы хотите поставить ограничение на клиента, следует использовать переменную:
set $limit_rate 196K;В Nginx также есть возможность установить порог отдачи, после которой ограничение войдет в силу. Также имеет смысл для потокового медиа (в этом случае первая указанная часть будет отдаваться без ограничений):
limit_rate_after 1m; #Ограничение скорости отдачи будет накладываться после 1МбЛоги
access_log /dev/null; #нам логи не нужны error_log /var/log/nginx/error.log; #а вот ошибки нужно знать! UPD: BuriK666 советует ставить acces_log off; Похоже у новых версиях баг з созданиям файла off — исправили.Сжатия gzip
gzip on; gzip_disable "msie6"; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;Для text/html не нужно, поскольку nginx для него сжимает автоматически.
Обработка клиентов
keepalive
keepalive_timeout = 30; keepalive_requests 100; #максимально от одного клиента Внимания! Это не копировать, а обдумать. Как подсказал gnomeby: Если держать keepalive для подгрузки статики, то будет примерно так: keepalive_timeout = 5; # keepalive_requests %максимальное количество подгрузок + 2-10%Ну и конечно от посетителей самой нагруженной страницы очень зависит. Тут, думаю, всё ясно.
Ограничения и отключения
reset_timedout_connection on;#Если клиент перестал читать, отвечать nginx сбрасывает соединение с ним client_body_timeout 10;#nginx будет ждать 10 секунд тело запроса от клиента, после чего сбросит соединение send_timeout 2; #Если клиент прекратит чтение ответа, Nginx подождет 2 секунды и сбросит соединение client_max_body_size 1m; #больше одного мегабайта запрос - ни-ни, хотя нужно на стороне приложения ограничиватьС этим нужно быть очень осторожным.
The end! Надеюсь, этот пост будет вам полезен.
sohabr.net
Зеленое Мышко: Введение в nginx, часть 2: Другие возможности
Данная статья была опубликована в электронном приложением к журналу "Системный администратор"- "Open Source #042"В первой части статьи я рассказал о базовых и наиболее часто применяемых возможностях nginx. Однако это малая часть того, что можно сделать с nginx. Во второй части своей статьи я расскажу о некоторых более продвинутых возможностях, которые используются в крупных и высоконагруженых проектах.
Failover и балансировка
Крупные проекты редко состоят из одного сервера приложений. Часто их два или больше, и возникает задача балансировки клиентов по этим серверам, а также выполнения failover — необходимо чтобы выход из строя одного из серверов не был заметен для клиентов. Простейший способ рещить эту задачу — dns round-robin, т.е. назначение доменному имени нескольких ip-адресов. Но это решение имеет ряд недостатков, и гораздо лучше выглядит решение балансировки запросов по бакендам на фронтенде nginx. В конфигурационном файле выглядит это примерно так:# объявляем upstream — список бакендов upstream backend { # перечисляем dns-имена или ip-адреса серверов и их «вес» server web1 weight=5; server 1.2.3.4:8080 weight=5; # а так можно подключаться к бакенду через unix-сокет server unix:/tmp/backend3 weight=1; } # конфигурация виртуального сервера server { listen <...>; server_name myserver.com; # отправляем все запросы из локейшена / в апстрим location / { proxy_pass http://backend; } } Запросы, приходящие к nginx, распределяются по бакендам соответственно указаному весу. Кроме того, можно сделать так, чтобы запросы с одних и тех же IP-адресов отправлялись на одни и те же серверы (для этого в upstream нужно указать директиву ip_hash). Так можно решить проблему с сессиями, но все же лучше найти какой-нибудь способ их репликации или (что еще лучше) использовать RESTful-подход. В случае, если один из серверов откажется принимать соединения или соединение к нему отвалится по таймауту, он на некоторое время будет исключен из upstream.Оптимизация nginx
1. Увеличение количества и объема буферов
Для хранения принятых запросов и еще не отданных ответов nginx использует буферы в памяти, а если запрос или ответ не помещается в них, nginx записывает его во временный файл (и пишет при этом предупреждение в log-файл). Поэтому необходимо установить такие размеры, чтобы в большинстве случаев не требовалось обращаться к временному файлу, а с другой стороны — чтобы буферы не использовали слишком много памяти.Для этого используются следующие параметры:client_body_buffer_size (по умолчанию: 8k/16k — в зависимости от архитектуры) — задает размер буфера для чтения тела запроса клиента. Обычно стандартного значения хватает, его требуется повышать, только если ваше приложение устанавливает огромные cookies.proxy_buffer_size (по умолчанию: 4k/8k) — задает размер буфера, в который будет читаться первая часть ответа, получаемого от проксируемого сервера. В этой части ответа находится, как правило, небольшой заголовок ответа. Стандартного значения обычно хватает.proxy_buffers (по умолчанию: 8 4k/8k) — задает число и размер буферов для одного соединения, в которые будет читаться ответ, получаемый от проксируемого сервера. Установите этот параметр так, чтобы большинство ответов от бэкенда помещалось в буферы.
2. Механизмы обработки соединений
Есть одна тонкость, касающаяся механизма обработки соединений, а именно — способ получения информации о событиях на сокетах. Существуют следующие методы:- select — стандартный метод. На большой нагрузке сильно нагружает процессор.
- poll — стандартный метод. Также сильно нагружает процессор.
- kqueue — эффективный метод, используемый в операционных системах FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 и Mac OS X. На 2-процессорных машинах под управлением Mac OS X использование kqueue может привести к kernel panic.
- epoll — эффективный метод, используемый в Linux 2.6+. В некоторых старых дистрибутивах есть патчи для поддержки epoll ядром 2.4.
- rtsig — real time signals, эффективный метод, используемый в Linux 2.2.19+. При больших количествах одновременных соединений (более 1024) с ним могут быть проблемы (их можно обойти, но на мой взгляд, лучше с этим не связываться).
- /dev/poll — эффективный метод, используемый в Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+ и Tru64 UNIX 5.1A+.
Включение gzip позволяет сжимать ответ, отправляемый клиенту, что положительно сказывается на удовлетворенности пользователя, но требует больше времени CPU. Gzip включается директивой gzip (on|off). Кроме того, стоит обратить на следующие важные директивы модуля gzip:
gzip_comp_level 1..9 — устанавливает уровень сжатия. Опытным путем выявлено, что оптимальные значения лежат в промежутке от 3 до 5, большие значения дают маленький выигрыш, но создают существенно большую нагрузку на процессор, меньшие — дают слишком маленький коэффициент сжатия.gzip_min_length (по умолчанию, 0) — минимальный размер ответа, который будет сжиматься. Имеет смысл поставить этот параметр в 1024, чтобы слишком малеьнике файлы не сжимались (т.к. эффективность этого будет мала).gzip_types mime-тип [mime-тип ...] - разрешает сжатие ответа методом gzip для указанных MIME-типов в дополнение к "text/html". "text/html" сжимается всегда. Имеет смысл добавить такие mime-типы как text/css, text/javascript и подобные. Разумеется, сжимать gif, jpg и прочие уже компрессированые форматы не имеет смысла.
Кроме того, существует модуль gzip_static, который позволяет раздавать уже сжатые статические файлы. В конфирурационном файле это выглядит так:
location /files/ { gzip on; gzip_min_length 1024; gzip_types text/css text/javascript; gzip_comp_level 5; gzip_static on; } При использовании такой конфигурации в случае запроса «/files/test.html» nginx будет проверять наличие «/files/test.html.gz», и, если этот файл существует и дата его последнего изменения больше, чем дата последнего изменения файла test.html, будет отдан уже сжатый файл, что сохранит ресурсы процессора, которые потребовались бы для сжатия оригинального файла.Оптимизация приложений
Существует очень полезный трюк, который позволяет указать разработчикам приложений, какие страницы нужно оптимизировать в первую очередь. Для этого потребуется в конфиге nginx указать новый формат лога:log_format my_combined '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$upstream_response_time "$host"' access_log /var/log/nginx/access_log my_combined; Переменная $upstream_response_time содержит время ответа бэкенда, поэтому в лог попадает время обработки каждого запроса бэкендом. Далее понадобятся два скрипта:1. /usr/local/bin/url_stats_report.sh:
#!/bin/sh echo "=== Requests which took most of the time ===" > /tmp/report.txt echo "overall time - number of requests - average time - url" >> /tmp/report.txt cat /var/log/nginx/*access.log | /usr/local/bin/url_stats.py >> /tmp/report.txt cat /tmp/report.txt | mail -s "url performance report" root 2. /usr/local/bin/url_stats.py:#!/usr/bin/env python import sys urls = {} try: while 1: line = raw_input() line_arr = line.split(" ") try: host = line_arr[-1] host = host[1:] host = host[:-1] url = line_arr[6] t = float(line_arr[-2]) #print host, url, t try: urls[host + url] = (urls[host + url][0] + t, urls[host + url][1] + 1) except KeyError, e: urls[host + url] = (t, 1) except ValueError, e: pass except EOFError, e: pass def sort_by_value(d): """ Returns the keys of dictionary d sorted by their values """ items=d.items() backitems=[ [v[1],v[0]] for v in items] backitems.sort(reverse=True) return [backitems[i][1] for i in range(0,len(backitems))] if (len(sys.argv) > 1): f = open(sys.argv[1], 'r') for k in f.readlines(): k = k.strip() try: print urls[k][0], urls[k][1], urls[k][0] / urls[k][1], k except: print 0, 0, k else: i = 0 for k in sort_by_value(urls): print urls[k][0], urls[k][1], urls[k][0] / urls[k][1], k i += 1 if i > 100: break Они не идеальны, но задачу выполняют: запуская /usr/local/bin/url_stats_report.sh (например, в postrotate утилиты logrotate), вы получаете наглядную картину, какие запросы занимают большую часть времени бэкенда.Кеширование
Незадолго до выхода этой статьи, Игорь Сысоев выпустил версию nginx 0.7.44 с экспериментальной поддержкой кеширования. Из-за большого количества багов, за которкое время было выпущено несколько версий, и в настоящее время последняя версия — 0.7.50 уже достаточно хорошо (хотя и не идеально) работает с кешированием. Однако это все еще экспериментальная функция, но я решил рассказать о ней, т.к. ее одень давно ждали многое администраторы, да и лично для меня она очень полезна. Сейчас nginx умеет кешировать на диске ответы от http и fastcgi запросов на бакенды, указывать ключ для кеширования, учитывать заголовки "X-Accel-Expires", "Expires" и "Cache-Control" и вручную устанавливать максимальное время жизни объекта в кеше. Обслуживанием кеша (очиста старых файлов, наблюдение за размером и т.п.) занимается специальный процесс cache manager. Положительной особенностью реализации является то, что при старте nginx cache manager начинает проверку кеша в фоне, благодаря чему nginx не делает то что называется «дает сквида», т.е. он не висит несколько минут проверяя кеш перед стартом. Я намеренно не указываю пример конфигурации, т.к. во-первых директивы могут еще поменяться, а во-вторых нужно глубокое понимание механизма кеширования, что требует вдумчивого чтения документации (http://sysoev.ru/nginx/docs/http/ngx_http_proxy_module.html#proxy_cache) и архивов рассылки nginx-ru.За кадром
В статье освещена лишь та часть возможностей nginx, которыми я пользуюсь чаще всего. За пределами рассказа остались такие вопросы, как поддержка SSI, работа с memcached, экспериментальный встроенный Perl и сторонние модули, реализующие дополнительную функциональность.blog-ru.greenmice.info