Как масштабировать php5 + MySQL выше 200 запросов / секунду?

16

Я настраиваю свою домашнюю страницу для повышения производительности, в настоящее время она обрабатывает около 200 запросов в секунду на 3.14.by, который использует 6 запросов SQL, и 20 запросов / секунду на 3.14.by/forum, который является форумом phpBB.

Как ни странно, цифры примерно одинаковы на некоторых VPS и выделенном сервере Atom 330.

Программное обеспечение сервера следующее: Apache2 + mod_php prefork 4 дочерних (здесь пробовали разные числа), php5, APC, nginx, memcached для хранения сессий PHP.

MySQL настроен на потребление около 30% доступной оперативной памяти (~ 150 МБ на VPS, 700 МБ на выделенном сервере)

Похоже, что где-то есть узкое место, не позволяющее мне подняться выше, какие-либо предложения? (то есть я знаю, что выполнение менее 6 SQL-запросов сделает это быстрее, но это не выглядит ограничивающим фактором, так как sqld потребляет не более нескольких% из-за кэшированных запросов)

Кто-нибудь проверял, что пинать preforked apache2 и оставлять просто nginx + php намного быстрее?

Еще несколько тестов

Small 40-byte static file: 1484 r/s via nginx+apache2, 2452 if we talk to apache2 directly. 
Small "Hello world" php script: 458 r/s via ngin+apache2.

Обновление: кажется, узким местом является производительность MySQL на кэшированных данных. Страница с одним SQL показывает 354 req / sec, с 6 SQL - 180 req / sec. Как вы думаете, я могу настроить здесь? (Я могу раскошелиться на 100-200Mb для MySQL)

[client]
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket      = /var/run/mysqld/mysqld.sock
nice        = 0

[mysqld]
default-character-set=cp1251
collation-server=cp1251_general_cs

skip-character-set-client-handshake

user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 3306
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /tmp
skip-external-locking

bind-address        = 127.0.0.1

key_buffer      = 16M
max_allowed_packet  = 8M
thread_stack        = 64K
thread_cache_size   = 16
sort_buffer_size    = 8M
read_buffer_size    = 1M

myisam-recover      = BACKUP
max_connections        = 650
table_cache            = 256
thread_concurrency     = 10

query_cache_limit       = 1M
query_cache_size        = 16M

expire_logs_days    = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet  = 8M

[mysql]
[isamchk]
key_buffer      = 8M

!includedir /etc/mysql/conf.d/
BarsMonster
источник
Почему вы используете Apache и nginx?
jamieb
Это обычная конфигурация, Apache2 to PHP и различные приложения, требующие инфраструктуры apache, nginx для уменьшения нагрузки на память apache2.
BarsMonster
На самом деле, я не понимаю вашу проблему. Ваш сайт в настоящее время медленный? Если да, то как медленно? И сколько вы хотите ускорить? Вы пытались профилировать какие-либо части вашего сайта, чтобы определить, где находится узкое место?
jamieb
Это в описании: теперь это на 180-200 запросов / секунду. Хотя для домашней страницы этого более чем достаточно, я хочу настроить эту настройку, чтобы другие сайты, основанные на той же кодовой базе, работали быстрее. В идеале я хочу насыщать 100 Мбит соединение динамическими страницами :-)
BarsMonster
2
«Количество запросов в секунду» не является значимым показателем в этом контексте. Мой нетбук мог обрабатывать "200 запросов в секунду". Вы должны сообщить нам, какое время отклика вы хотите достичь при такой скорости соединения.
jamieb

Ответы:

29

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

MySQL

  • query_cache_type = 1 - кеш запросов SQL включен. Если задано значение 2, запросы кэшируются только в том случае, если им передается подсказка SQL_CACHE. Аналогично с типом 1 вы можете отключить кеш для конкретного запроса с помощью подсказки SQL_NO_CACHE
  • key_buffer_size = 128M (по умолчанию: 8M) - буфер памяти для индексов таблиц MyISAM. На выделенных серверах установите для параметра key_buffer_size не менее четверти, но не более половины общего объема памяти на сервере.
  • query_cache_size = 64M (по умолчанию: 0) - размер кеша запросов
  • back_log = 100 (по умолчанию: 50, макс .: 65535) - очередь ожидающих запросов на подключение. Имеет значение только тогда, когда есть много соединений в короткие сроки
  • join_buffer_size = 1M (по умолчанию: 131072) - буфер, который используется при полном сканировании таблицы (без индексов)
  • table_cache = 2048 (по умолчанию: 256) - должно быть max_user_connections умножено на максимальное количество JOIN, которое содержит ваш самый тяжелый SQL-запрос. Используйте переменную open_tables в часы пик в качестве руководства. Также посмотрите на переменную "open_tables" - она ​​должна быть близка к "open_tables"
  • query_prealloc_size = 32K (по умолчанию: 8K) - постоянная память для разбора и выполнения операторов. Увеличение при наличии сложных запросов
  • sort_buffer_size = 16M (по умолчанию: 2M) - помогает в сортировке (операции ORDER BY и GROUP BY)
  • read_buffer_size = 2M (по умолчанию: 128K) - Помогает при последовательном сканировании. Увеличьте, если есть много последовательных сканирований.
  • read_rnd_buffer_size = 4M - помогает ускорить чтение таблицы MyISAM после сортировки
  • max_length_for_sort_data - размер строки для хранения вместо указателя строки в файле сортировки. Можно избежать случайного чтения таблицы
  • key_cache_age_threshold = 3000 (по умолчанию: 300) - время для хранения кеша ключей в горячей зоне (до того, как она разогреется)
  • key_cache_division_limit = 50 (по умолчанию: 100) - включает более сложный механизм удаления кеша (два уровня). Обозначает процент для нижнего уровня. delay_key_write = ALL - буфер ключа не очищается для таблицы при каждом обновлении индекса, а только при закрытии таблицы. Это значительно ускоряет запись на ключи, но если вы используете эту функцию, вы должны добавить автоматическую проверку всех таблиц MyISAM, запустив сервер с параметром --myisam-recovery = BACKUP, FORCE
  • memlock = 1 - блокировка процесса в памяти (для уменьшения подкачки / выгрузки)

апаш

  • изменить метод порождения (например, в mpm)
  • отключить журналы, если это возможно
  • AllowOverride None - по возможности отключайте .htaccess. Он останавливает apache для поиска файлов .htaccess, если они не используются, поэтому сохраняет запрос поиска файла
  • SendBufferSize - установить в ОС по умолчанию. В перегруженных сетях вы должны установить этот параметр близко к размеру самого большого файла, обычно загружаемого
  • KeepAlive Off (по умолчанию включен) - и установите lingerd для правильного закрытия сетевых подключений и работает быстрее
  • DirectoryIndex index.php - сохранить список файлов как можно более коротким и абсолютным.
  • Опции FollowSymLinks - для упрощения процесса доступа к файлам в Apache
  • Избегайте использования mod_rewrite или хотя бы сложных регулярных выражений
  • ServerToken = прод

PHP

  • variable_order = "GPCS" (если вам не нужны переменные окружения)
  • register_globals = Off - помимо угрозы безопасности, он также влияет на производительность
  • Держите include_path как можно меньше (избегая дополнительных поисков файловой системы)
  • display_errors = Off - отключить отображение ошибок. Настоятельно рекомендуется для всех производственных серверов (не отображает ужасные сообщения об ошибках в случае возникновения проблем).
  • magic_quotes_gpc = Off
  • magic_quotes _ * = Off
  • output_buffering = On
  • Отключите ведение журнала, если это возможно
  • expose_php = Off
  • register_argc_argv = Off
  • always_populate_raw_post_data = Off
  • Поместите файл php.ini, где php будет искать его в первую очередь.
  • session.gc_divisor = 1000 или 10000
  • session.save_path = "N; / path" - для больших сайтов рассмотрите возможность его использования. Разбивает файлы сессий на подкаталоги

ОС твики

  • Монтируйте используемые жесткие диски с опцией -o noatime (без времени доступа). Также добавьте эту опцию в файл / etc / fstab.
  • Настройте / proc / sys / vm / swappiness (от 0 до 100), чтобы увидеть, что дает лучшие результаты
  • Использовать диски RAM - монтировать --bind -ttmpfs / tmp / tmp
Иван Пеевски
источник
Это хороший список, у меня уже было большинство из них, и когда я добавил оставшиеся вещи, производительность не увеличилась. Похоже, узкое место где-то между PHP и MySQL не может обрабатывать более 800 запросов в секунду из кэша запросов ...
BarsMonster
Хорошо, как вы подключаетесь к базе данных (mysql_pconnect () вместо mysql_connect ())? Вы используете постоянные соединения? попробуйте оба пути ...
Иван Пеевски
Я уже на pconnect, и пул соединений включен в php.ini ...: -S
BarsMonster
Просто для полноты я бы попробовал просто подключиться. Я видел случаи (особенно в нагрузочном тестировании), где это работает лучше.
Иван Пеевски
1

Если узким местом является не процессор, то его IO - либо сеть, либо диск. Итак ... вам нужно посмотреть, сколько IO происходит. Я бы не подумал, что это сеть (если вы не используете полудуплексный канал 10 Мбит / с, но стоит проверить коммутатор на случай, если функция автоопределения не выполняет свою работу правильно).

Это оставляет дисковый ввод-вывод, который может быть важным фактором, особенно на VPS. Используйте sar или iostat, чтобы взглянуть на диски, а затем Google, как найти более подробную информацию, если ваш диск интенсивно используется.

gbjbaanb
источник
Да, сеть не является проблемой - при запуске ab с локального сервера производительность остается неизменной. Я проверил время iowait - оно меньше 0,01% - в основном все находится в дисковом кеше, и нет никакой записи на диск, вовлеченной в обработку запроса (все журналы отключены).
BarsMonster
1

Я хотел бы изучить кеширование с помощью Nginx ( memcached ) или Varnish .

По крайней мере, вы должны сервер статических файлов с Nginx, как сказал SaveTheRbtz.

Espennilsen
источник
Это динамические страницы, поэтому я бы предпочел не кэшировать их.
BarsMonster
1
memcached не является традиционным приложением для кэширования и может творить чудеса для динамических страниц. Он находится между БД и вашим приложением. Ваше приложение сначала запрашивает memcached для объекта, если его там нет, то он загружается из БД. В итоге вы используете ОЗУ для обслуживания запросов к БД, а не гораздо более медленное постоянное хранилище на БД.
jamieb
Memcache можно использовать с nginx, это известная функция. Медленное постоянное хранилище не используется, оно все в кеше запросов в MySQL.
BarsMonster
Memcached и MySQL кеш запросов не совсем сопоставимы; они даже не делают то же самое. Вы довольно быстро сбиваете с толку почти все предложения, размещенные здесь, не удосужившись понять их. Я бы порекомендовал вам быть более открытым.
jamieb
Я четко понимаю разницу между memcached и MySQL кеш запросов. Но из-за того, что все находится в кеше запросов со 100% коэффициентом попадания, я бы не назвал это «медленным постоянным хранилищем». Оригинальный вчерашний ответ был об использовании NginX + Memcached, который является довольно распространенным сценарием для кэширования целых страниц. Кэширование отдельных объектов - это другой, совершенно другой сценарий. Хотя использование memcached перед MySQL находится на рассмотрении, сейчас я думаю о том, чтобы получить больше сока без него (поскольку это потребовало бы немало изменений кода).
BarsMonster
1

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

Olivers
источник
Производительность остается прежней, даже если я запускаю ее с самого сервера. Независимо от того, как выполняются одновременные соединения - 10 или 50. Нагрузочное тестирование проводится через ab -c 10 -t 10
BarsMonster
1

Для меня это звучит так, как будто вы подключаетесь к максимальному количеству соединений, которые пропускает Apache. Посмотрите на вашу конфигурацию Apache. Увеличение лимита сервера и максимальное количество клиентов должно помочь, если вы еще не связаны каким-либо другим ограничением, таким как ввод-вывод или память. Посмотрите на значения, присутствующие для mpm_prefork_module или mpm_worker_module и скорректируйте в соответствии с вашими потребностями.

ServerLimit 512
MaxClients 512
Эрик Джиберти
источник
Ну, действительно ли мне это нужно при условии, что у меня есть nginx перед apache2, поэтому я верю, что нет особого смысла иметь больше, чем физические ядра * 2 процесса Apache2 ....
BarsMonster
Просто проверил это. Увеличение числа процессов Apache2 с 4 до 16 не улучшило производительность вообще (оно даже упало на 0,5%). Увеличение числа работников nginx до 2 или 4 ничего не улучшило.
BarsMonster
1
Если ваши данные довольно статичны, то есть не обновляют каждую другую загрузку страницы, вы можете увеличить ваш query_cache. MySQL таким образом будет удерживать результирующий набор и извлекать из памяти. Однако, если кешируемая таблица получает какие-либо записи в течение этого времени, она делает кеш недействительным (даже если на данные это не влияет), что приводит к потере памяти.
Эрик Джиберти
Прямо сейчас я вижу 100% коэффициент попадания в кеш запросов, а MySQL все еще чувствует себя медленным ...
BarsMonster
1
Добавьте skip-name-Resolution в ваш конфигурационный файл MySQL. Это сохранит поиск DNS при каждом подключении к серверу. Недостатком здесь является то, что все соединения должны быть заблокированы по IP (при условии, что вы не используете «%»). Если SQL находится на том же сервере и доступ к нему не требуется нигде, кроме localhost, вы также можете добавить пропуск сети, чтобы уничтожить весь стек TCP / IP. Тем не менее, я думаю, что узким местом является Apache.
Эрик Джиберти
0

Эта нагрузка создается инструментом или реальными нагрузками?

Вы можете проверить memcached. Я видел проблемы с высокой скоростью соединения, что приводило к задержке в приложении.

Если вы используете генератор нагрузки, что вы получаете, когда нажимаете на небольшую статическую страницу?

Во время загрузки вы можете проверить сетевой стек на наличие условий TIME_WAIT. Возможно, вы заполняете свою очередь соединения.

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

jeffatrackaid
источник
Он протестирован через ab-c 10 -t 10 URL, который я сравнивал с самим сервером, поэтому проблема с сетью не должна быть. Я разместил больше тестов по вашему запросу.
BarsMonster
Я не стал бы тратить слишком много усилий на настройку с помощью ab. Вы можете обнаружить, что это не очень хорошо отражается на реальных показателях. Что вы можете сделать, это проанализировать ваше приложение и протестировать каждый компонент. Например, поразите сервер apache напрямую очень маленькой статической страницей. Это даст вам представление о вашем максимальном требовании / сек на бэкэнде. Поместите nginx вперед, повторите тестирование, вызывая тот же бэкэнд-файл. Затем протестируйте простую php-страницу типа «hello world». Иногда все слои могут маскировать что-то простое. Также следите за соединениями во время теста. Убедитесь, что ваш сетевой стек не заполняется.
Jeffatrackaid
Я сделал эти тесты вчера, и они находятся в обновленном оригинальном описании вопроса. Кроме того, тесты проводятся на локальном хосте, поэтому сеть не является проблемой.
BarsMonster
Сеть может быть проблемой, даже если это сделано на локальном хосте. В вашем случае это маловероятно, но это может вызвать проблемы. По крайней мере теперь у вас есть верхний предел ~ 450 рэк / сек с вашей текущей настройкой PHP. Следующий шаг - добавить вызов базы данных и посмотреть, как это изменится. Мне нравится разбивать это на части при выполнении высокоуровневой настройки, так как это может помочь вам точно определить слой, вызывающий большинство проблем.
Jeffatrackaid
-1

В 99% случаев подобные проблемы будут отслеживаться в базе данных. Убедитесь, что ваши индексы попадания в первую очередь. Если это не сработает, начните кэшировать все, что можете.


источник
Это все индексы, и, как я уже говорил, он даже попадает в кеш запросов MySQL в 100% случаев
BarsMonster,
-1

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

Кроме того, попробуйте проанализировать все ваши запросы с помощью EXPLAIN (и почему бы не профилировать ваши запросы с помощью SHOW PROFILE?).

Kedare
источник
Все запросы используют индексы. MySQL Connection pool используется.
BarsMonster