Пользователь
0,0
рейтинг
14 июля 2015 в 19:05

Разработка → 36 млн запросов в час, 10000+ постоянно работающих клиентов, на одном сервере, nginx+mysql из песочницы

Сложилась ситуация, что участвую в проекте, который работает с достаточно большой нагрузкой. Как уже написал — 36 млн запросов в час. Я много чего прочитал и перепробовал за последний месяц, настраивая сервер; хотелось бы просто сжато и компактно выдать тезисно то, что работает хорошо в такой конфигурации.

Первое, что я заметил — множество советов как все настроить под большую нагрузку. Читайте их внимательно, обычно в тексте найдете, что речь про «высокую нагрузку» в 15-20 тысяч клиентов в сутки. У нас клиентов примерно миллион, активных, ежедневных.

У нас нет денег и мы все делаем за свой счет, поэтому экономим. Итог — весь миллион клиентов обслуживается на одном сервере, вот на таком — EX-60 на hetzner.

Мы случайно сделали себе аналог DDoS через своих клиентов и в результате настроек, когда было по 4000 php процессов, загрузка ОС так же под 4000, я успел перепробовать множество конфигураций и найти наиболее работающие. С ошибкой в софте справились, теперь эти 10-12 тысяч запросов в секунду обрабатываются с загрузкой load average: 3,92, 3,22, 2,85. Не единичка, конечно, но для одного сервера считаю хорошим результатом.

Операционка — CentOS 7.1, 64 бита. Минимальная инсталляция, плюс iptables, nginx, php-fpm, mysql. Ядро 4-й версии, из kernel-ml.

Тюнинг настроек ядра под большой напор tcp коннектов:

/etc/sysctl.conf
fs.file-max = 1000000
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.tcp_max_orphans = 65536
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1800
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_max_syn_backlog = 65536
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_mem = 50576 64768 98152
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_orphan_retries = 0
net.ipv4.tcp_syncookies = 0
net.ipv4.netfilter.ip_conntrack_max = 1048576
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_congestion_control = htcp
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.route.flush=1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.lo.rp_filter = 1
net.ipv4.conf.eth0.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.lo.accept_source_route = 0
net.ipv4.conf.eth0.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rfc1337 = 1
net.ipv4.ip_forward = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.core.somaxconn = 262144
net.core.netdev_max_backlog = 1000
net.core.rmem_default=65536
net.core.wmem_default=65536
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

Тюнинг лимитов по файлам, так как ни каких юзеров на сервере нет, то особо не заморачиваемся:

/etc/security/limits.conf
* soft nproc 65535
* hard nproc 65535
* soft nofile 100000
* hard nofile 100000
root soft nofile unlimited
root hard nofile unlimited

Монстры знают, но я давно не администрировал и был не в курсе, что * не работает на root, и его надо отдельно тюнинговать.

На этом с ядром всё.

Настройки мускула. Установлена percona-56.

Выбор был сделан в итоге на InnoDB, пробовали TokuDb, но на больших объемах постоянных инсертов, а их у нас 95% из 36 млн в час. InnoDB ведет себя лучше, тесты самой перконы говорят о том же.

Настройки mysql:

/etc/my.cnf
[mysql]
port = 3306
socket = /var/lib/mysql/mysql.sock
[mysqld]
user = mysql
default-storage-engine = InnoDB
socket = /var/lib/mysql/mysql.sock
pid-file = /var/lib/mysql/mysql.pid
key-buffer-size = 32M
myisam-recover = FORCE,BACKUP
max-allowed-packet = 16M
max-connect-errors = 1000000
skip-name-resolve
datadir = /var/lib/mysql/
tmp-table-size = 32M
max-heap-table-size = 32M
query-cache-type = 0
query-cache-size = 0
max-connections = 15000
thread-cache-size = 5000
open-files-limit = 150000
table-definition-cache = 1024
table-open-cache = 50000
innodb-flush-method = O_DIRECT
innodb-log-files-in-group = 2
innodb-log-file-size = 2G
innodb-file-per-table = 1
innodb-buffer-pool-size = 10G
innodb_flush_log_at_trx_commit = 0
log-error = /var/log/mysql/mysql-error.log
log-queries-not-using-indexes = 0
slow-query-log = 1
slow-query-log-file = /var/log/mysql/mysql-slow.log

Обязательно отключаем при такой нагрузке query-cache. Он будет реально тормозить всю систему. Впрочем, поиграйтесь, возможно в вашем случае нет, но во многих тестах и текстах встречал этот момент, проверил у себя — так и есть, с отключенным работает быстрее.

skip-name-resolve тоже дает хороший прирост.

Дополнительные настройки по отношению к стандартным для nginx:

fastcgi_params
fastcgi_param REDIRECT_STATUS 200;
fastcgi_buffer_size 4K;
fastcgi_buffers 64 4k;

nginx тюним под наши нужды:

nginx.conf
user nginx;
worker_processes 8;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

worker_rlimit_nofile 150000;

events {
worker_connections 8000;
multi_accept on;
use epoll;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr — $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

gzip off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
reset_timedout_connection on;
server_tokens off;
client_body_buffer_size 128k;

include /etc/nginx/conf.d/*.conf;

}

Ядер 8, поэтому и worker-процессов 8, по 8000 на брата, все-равно больше 64к не обслужить за раз. Будет небольшая очередь, если будет больше одновременных коннектов.

В сайте с php-fpm общаемся через сокеты:
/etc/nginx/conf.d/site.conf
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
fastcgi_send_timeout 180s;
fastcgi_read_timeout 180s;

Основное по конфигурации php-fpm:

/etc/php-fpm.d/www.conf
listen = /var/run/php-fpm/php-fpm.sock
pm = ondemand
pm.max_children = 4000
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_requests = 0

ondemand мало где описан, но он лучше чем dynamic под большой нагрузкой. А static — это, конечно, убийство для сервера, не понравилось сильно.

ondemand начинает с 5, при необходимости наращивается, но в отличии от dynamic с уменьшением нагрузки не убивает процессы, чтобы потом опять наращивать, а просто на пике фиксирует значение и переводит ненужный в режим ожидания. И если вдруг нагрузка опять растет — процессы уже готовы, никого не надо запускать с нуля.

pm.max_requests = 0 помогает боротся с утечками памяти, в стороннем софте.

Собственно, так мы и обслуживаем 36 млн в час, из которых 95 процентов — передача к нам данных и запись их в БД. На 2.8 миллиарда запросов у нас сейчас от 10 до 16 slow_query, каждый не больше 10 секунд, причем все они — селекты с джойнами по многим полям и таблицам. Остальные запросы отрабатывают моментально.

Вместо php-fpm компилировал и использовал hhvm одно время, действительно работает шикарно, значительно быстрее php-fpm, но есть беда — каждые 30-40 минут падает, причем наглухо.

В git разработчикам написал, пока ничем не смогли помочь, причин не знают. В итоге сидим на php-fpm, версия 5.6.

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

Думаю, кому-то будет полезна эта информация о настройках вся в одном месте.
@RUnnerTomsk
карма
26,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (101)

  • +10
    rusage каждого из запросов меньше 1мс? Из чего же состоят ваши скрипты? Просто mysql_query без авторизации?
    • 0
      Авторизация по каждому запросу, затем update в БД, затем insert данных с replace on duplicate
      • 0
        БД от такого количества запросов не падает? Одно дело принять по сети 10К запросов в секунду, другое дело на диск их записать.
        • 0
          В среднем приходит от 200 до 500 байт данных


          Это в джейсоне. То есть самих данных <200 байт в запросе. 10к рек-сек * 200 байт == 2МБ сек — скорость записи на диск. То есть даже любой самый хиленький hdd справится.
          • 0
            это если индексов нет
            • 0
              5-ть индексов, 3 по одному полю, один по двум полям, один по трем полям.
        • 0
          Мне кажется, или в случае чего, можно ведь в redis эти однородные инсерты запихивать, и раз в несколько секунд делать один большой запрос к БД на обработку.
          • 0
            Если в редис, то оперативка закончится за 1 день
            • 0
              Можно заюзать ssdb, и писать с той же скоростью и тем же протоколом, что и редис, но пока есть место на диске (я пробовал на уровне 40+Гб где-то)
              • +1
                Можно заюзать ssdb


                А можно и MySQL, что и сделал автор. Зачем ssdb? Зачем редис?
  • +3
    Расскажите еще про pm.max_requests = 0. Это после каждого запроса интерпретатор перезапускается (т.е. всего 10000 раз в секунду)? Это же невозможно. Если наоборот, отключает перезагрузку интерпретатора, то как это «помогает бороться с утечками памяти»?

    Ну pm.max_children = 4000 — это что-то невероятное. Если каждый процесс кушает по 20 мегабайт, то это уже 80 гигабайт памяти.
    • +4
      Расскажите еще про pm.max_requests = 0. Это после каждого запроса интерпретатор перезапускается (т.е. всего 10000 раз в секунду)?

      Там наоборот, процессы не перезапускаются вообще:
      The number of requests each child process should execute before respawning. This can be useful to work around memory leaks in 3rd party libraries. For endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. Default value: 0.

      Так что у автора никакой борьбы с утечками нет :)
      • +1
        Ага, сейчас нет, тут прогнал маленько. Пока боролись — было было 500. Сейчас тьфу тьфу тьфу, все стало хорошо и поставил 0 в текущем конфиге.
    • 0
      4000 держит, но всё упирается вы этом случае в CPU, не справляется.
      Сейчас хватает максимум 400 процессов.
  • +15
    Инфы мало. Что за запросы? Какой протокол? Какой средний размер запроса? Каждый ли реквест пишется в базу? Как в базу пишете? Один поток? Много потоков? Какого рода запросы? Инсерты? Инсерты с селектом? Еще что-то? Сколько данных в сек пишется на диск? Сколько читается? Какой трафифик на сервер туда-сюда? Сколько самих реквестов в базу? Как деплоймент делаете при такой нагрузке? Отрубаете сервер?

    Вполне может оказатся что после ответов 10к рек-сек это мало для такого сервака =).
    • +5
      Подпишусь под вопросом.
      Какова логика скриптов? Какие объемы данных? Какие индексы? Количество записей?
      Мы все были бы очень признательны за чуть больший объем инфы.
    • 0
      Запросы входят через http в json формате. В среднем приходит от 200 до 500 байт данных, парсится, и уже в нужном виде вставляется в БД.
      Каждый пишется в БД, сначала идет select для проверки токена авторизации, если все ок — делается update для выставления флага удаления для потерявших актуальность записей, затем делается insert в БД с replace в случае если такой ключ уже имеется.
      Соответственно есть еще один демон, на джаве, который висит на отдельном порту, слушает сокет, и клиент сначала в него кидает пару логин/пароль, получает токен авторизации, и уже с ним через http в json формате сливает данные на сервер.
      Коряво, но когда-то было сделано такое решение, пока его не меняем.
      • 0
        Однажды на практике убедились, что если часть запросов из бекенда перенести на сторону nginx ( например модули на perl исполняемые nginx-ом) можно получить почти на порядок большее qps к БД. А лучше, наверное, модули на Lua. Правда все упирается в специфику данных и саму базу. Но мне кажется, вам стоит попробовать. Если интересно могу в личку скинуть наш пример. Нашей задачей был сбор статистических метрик с распаковкой данных и по возможности быстрой записью в БД.

        Сижу на PgDay, пишу с телефона, прошу прощения за опечатки.
  • +1
    А как же отказоустойчивость?
    • +1
      Хромает. Думаем над этим. Пока не сбоит, но понятно что это не может быть вечным — один сервак у хетцера уже сожгли — винты вышли из строя.
      • 0
        Винты — это болезнь хецнера, по опыту (парк в 30+ серверов в течении пары лет) — самая большая. Хотя у нас архитектура позволяла потерять несколько серверов, но в итоге пришли к тому, что заказывали новые винты и мониторили состояние работающих через smartctl. Это позволяло отловить умирающие винты заранее и принять превентивные меры.
  • +1
    «Итог — весь миллион клиентов обслуживается на одном сервере»
    Учитывая что Хетцнер использует ненадежное железо десктоп класса, которое периодически падает — хороший выбор, успехов)

    Касательно нагрузки — действительно 400 инсертов в секунду делаете на одном DB сервере?
    • +1
      Честно говоря, 400 инсертов в секунду для одного сервера MySQL это ничто :) Другое дело, что тут всё на одной железяке.
    • +3
      а как вы посчитали что 400?! 10к запросов в секунду из которых 95% это инсерты, то есть 9500 инсертов же
    • 0
      12k qps видел, выше — не попадал в тот момент на сервер ) Я редко на него захожу.
      По железу — один сервак сожгли, винты посыпались, через 40 минут хетцер дал новый, переехали быстро.
      Есть там и enterprise решения, но дорого для нас пока что.
      • 0
        Спасибо за уточнение.
        По хостингу как я и говорил оказалось.
  • +7
    Подскажите пожалуйста, а каким образом вы подсчитали количество запросов в час?
    • +2
      mysql ведет статистику запросов. Берем количество, делим на время.
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          В мускул. Но они все приходят через веб. Так что можно уровнять я думаю.
          • +2
            Авторизация по каждому запросу, затем update в БД, затем insert данных с replace on duplicate

            Если даже принять, что авторизация на сервер, а не в базу, то по вашим же словам на каждый запрос на веб-сервер приходится 2 запроса в БД. Соответсвенно, количество запросов к веб-серверу в 2 раза меньше. Правильно я понимаю?
            • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              Не совсем. Клиент перед отправкой данных сначала запрашивает версию API на сервера, это ему nginx отдает, затем запрашивает контрольную сумму клиента на сервере, это ему так же nginx отдает.
              Если что-то не совпало — автообновляется. А если совпало — то идет передача данных. Так что разница реальная по отношению к SQL запросам где-то меньше процентов на 20-30%.
  • 0
    А всякие nginx_mysql_module не рассматривались, раз в php почти ничего кроме запросов к базе и нет?
    • 0
      Да, это следующий шаг, сейчас изучаем как писать модули под nginx, думаем написать модуль и вообще уйти от php.
      • –11
        Node.js
        • +2
          У него HTTP-сервер по скорости сильно хромает.
        • 0
          ReactPHP даст тот же профит примерно. Или PHP-PM. Полный отказ от прослойки дополнительной профита даст явно больше.
          • 0
            Скорее всего там не используются тяжелые фреймворки, да и фрейморки вообще, поэтому отличие будет небольшим. Но зато можно будет кэшировать prepared statements и set names.
      • 0
        Если вам только запросы в mysql, то достаточно самого nginx_mysql_module. Если все же какая-то логика, то почему бы не lua-nginx-module + lua-resty-mysql?
  • +6
    в секунду обрабатываются с загрузкой load average: 3,92, 3,22, 2,85. Не единичка, конечно, но для одного сервера считаю хорошим результатом.

    Единичка и не нужна, на 8-ядерной машине 100% загрузка всех ядер — это 8.

    ondemand мало где описан

    ondemand описан как минимум в комментариях www.conf:
    ondemand — no children are created at startup. Children will be forked when new requests will connect.


    но он лучше чем dynamic под большой нагрузкой..

    Может быть все дело в том, что в режиме ondemand php смотрит на параметры pm.max_children и pm.process_idle_timeout. Параметры pm.start_servers, pm.min_spare_servers используются только в режиме dynamic.

    но в отличии от dynamic с уменьшением нагрузки не убивает процессы,

    pm.process_idle_timeout — The number of seconds after which an idle process will be killed.

    А static — это, конечно, убийство для сервера, не понравилось сильно

    Правильно, потому что он запускает конкретное количество инстансов. И с вашим количеством в 4000 инстансов серверу потребуется около 80 гигов памяти.
    Такая же ситуация, в принципе, будет и с текущими настройками в пике (если пхп запустит все 4000 инстансов, указанных в pm.max_children).

    pm.max_requests = 0 помогает боротся с утечками памяти, в стороннем софте.

    Как уже написали выше, pm.max_requests = 0 значит совсем противоположное.
    • 0
      Ага, спасибо. Я давно отошел от админства, со времен freebsd 3-й мало что админил из юниксов. Пришлось вспоминать, изучать методом тыка.
      По таймауту процессы и убиваются — сейчас у меня 30 секунд стоит.
      Про max_request выше писал, прогнал, — было в конфиге 500 когда боролись с утечками. Как победили — стало 0, держит стабильно, память не исчезает в никуда.
  • +1
    Имхо на таком кол. запросов нужно логи вырубать, ну или не на HDD их сбрасывать.
    Не знаю какие у вас там запросы, но если есть запросы, как вы сказали, только на запись, советую для этих запросов дополнительно глянуть сюда php.net/manual/en/function.fastcgi-finish-request.php это поможет немного легче жить nginx-у.
    Ещё как вариант, возможно, имеет смысл посмотреть на модуля для того же nginx-а, которые умеют писать напрямую в DB, с пост обработкой на php.
    • 0
      О логах — я о логах запросов, а не ошибок.
    • 0
      Да, это решение мы на позапрошлой неделе сделали. Делаем финиш и дальше уже все работы по обработке данных и вставке их в БД.
      И модуль да, хотим написать свой модуль под nginx чтобы уйти от php вообще.
      • 0
        Зачем тогда писать модуль для nginx-а, что ПОЛНОСТЬЮ уйти от php? Перепишите тогда полностью на на C++ и все…
        • 0
          Полностью уйти на приеме данных.
          Веб-морда для пользователей остаётся, она вообще не symfony сделана.
          Была идея по типу демона авторизации, что на java сделан, сделать и обработку входящих данных так же.
          Но пока всё это под вопросом.
          • 0
            посмотрите тогда в сторону nginx + lua, возможно это то, что вам нужно
      • +1
        del
  • +3
    Зачем так много?
    fastcgi_send_timeout 180s;
    fastcgi_read_timeout 180s;
    • 0
      Когда подсчитывается статистика и удаляются записи устаревшие, в этот момент дорогой мускул лочит таблицу, и если в этот момент, ночной, кто-то юзает веб-морду сервиса — чуть чуть ждет и получает результат, вместо ошибки «что-то пошло не так на нашем сервисе».
      Корявый момент, пока так его обошли.
      Ночью делаем delete from where del_flag=1, а затем alter table чтобы уменьшить объем данных и облегчить процесс впихивания всей этой беды по максимуму в кэш InnoDb, а то база разрастается до 20 млн и выше записей, в каждой записей по 20 полей, и они активно используются в достаточно больших select-ах с несколькими джойнами, на веб-морде, и мускул начинает подтупливать.
      • 0
        Ночью делаем delete from where del_flag=1, а затем alter table чтобы уменьшить объем данных и облегчить процесс впихивания всей этой беды по максимуму в кэш InnoDb

        Партиционирование таблиц не рассматривали? ALTER TABLE достаточно дорогая операция…
        • 0
          Пока не придумали как применить. Хотя слова эти витают в воздухе постоянно )
          • 0
            А как вы конкретно уменьшаете размер таблицы? Не выходит сделать так, чтобы лишние данные попадали в другую партицию?
            • 0
              Удаляем данные, делаем alter — оно дефрагментирует, так как InnoDb.
        • +1
          для ALTER TABLE на больших объемах, советую посмотреть в сторону www.percona.com/doc/percona-toolkit/2.2/pt-online-schema-change.html
          • 0
            Спасибо!!!
            Будем смотреть!
  • +1
    innodb_flush_log_at_trx_commit=0

    Аварийно сервер пробовали отключать и смотреть что с базой после этого?
    • 0
      Если сильно сильно не повезет — потеряем пару секунд транзакций, для нас это не критично — данные от клиента прийдут заново очень быстро.
  • 0
    Из статьи не понятна структура таблиц/запросов. Но вероятнее всего под такой нагрузкой хранятся пары вида «ключ, значение».
    Если так, то не лучше ли перейти на решения а-ля redis?
    Если по каким-то причинам no-sql решения не подходят, а структура запросов INSERT одинакова гоните их в mysql через fifo pipe напрямую.
    • 0
      Вкратце — таблица пользователей, с их данными.
      Вторая таблица, в которую идут основные инсерты — ИД юзера, плюс ИД записи данных этого юзера, и 20 полей данных этой записи.
      У каждого юзера в среднем 18-20 записей данных.
      Плюс вспомогательные таблицы для расчета некоторых полей, часть полей приходит от клиента, часть — калькулируется и затем вставляется в БД в виде записи окончательно.
      • 0
        Данные вносятся полагаю через form input? Если да, то
        в которую идут основные инсерты — ИД юзера, плюс ИД записи данных этого юзера
        навскидку сохраняя структуру, логику, не уходя от mysql и не писав собственного модуля для nginx:

        — добавляем в nginx модуль HttpFormInputModule от taobao

        — делаем
        log_format mylog 'INSERT IGNORE INTO `log`.`access` (`user`, `data1`, `data2`) VALUES ("$post_user","$post_data1","$post_data2");\n';
        логируем в нужном локейшине

        — создаём fifo-шку
        # mkfifo /var/log/nginx/fifo.mylog && chmod 666 /var/log/nginx/fifo.mylog

        — пайпим данные напрямую без php-шной обработки
        # tail -F /var/log/nginx/fifo.mylog | mysql -pMYSQL_PASS

        Вероятнее всего, для того чтобы данные полились придётся рестратануть (НЕ reload) nginx, чтобы он нашёл fifo.
        С таким потоком данных по поводу flush'a буферов можно не заморачиваться.
        Тем самым input-переменные не требующие дополнительной обработки льются напрямую без всякого php.
        • 0
          Интересное решение. Что с безопасностью и фильтрацией вводимых данных напрямую в базу? $post_data2 = '1");\nDROP DATABASE;\n'; — ну так навскид :)
          • 0
            Код модуля смотрел очень давно, не помню. Но если обработки в нём и нет, то добавить её не так сложно.
            Самим модулем пользовался на нагрузке около 100k per sec, падений не было.
        • 0
          Нет, данные идут из клиентов, сразу POST запрос в json формате по нужному урлу.
          • 0
            Логики вышеописанного это не меняет, запросы всё-равно попадут в ngx_http_form_input_post_read(ngx_http_request_t *r).
            Только разбор JSON опять же придётся отдать на откуп этому модулю.
            • 0
              Спасибо, очень интересно! У нас есть идея разделить данные на те что надо дообрабатывать, всмысле сделать рассчеты по ним, и те что не надо, возможно это решение будет в тему для тех данных что нужно просто залить и сохранить.
  • 0
    > hhvm

    По нашим тестам оно помогает заметно только при PHP <= 5.3, емнип.
    На >= 5.5 лучше всего идет обычный PHP-FPM с OPCache.
    • 0
      По ощущениям — похоже так и есть.
  • 0
    use epoll;


    Nginx уже несколько лет как умеет самостоятельно выбирать оптимальный метод. Есть ли реальная необходимость в этой строке, чем руководствовались при её добавлении?
    • 0
      чтобы наверняка )
    • 0
      Ну раз уж на то пошло,
      worker_processes 8;

      тут тоже можно заменить на auto где-то с 1.2 ветки. Но никто не запрещает их описывать. как и кучу других параметров, неплохо настроенных искаропки. Хотя бы, чтобы быть уверенным, что у тебя они верно выставлены.
  • +1
    Вместо php-fpm компилировал и использовал hhvm одно время, действительно работает шикарно, значительно быстрее php-fpm, но есть беда — каждые 30-40 минут падает, причем наглухо.

    Та же самая проблема, побороть так и не смог, хотя производительность мне понравилась.
  • +1
    А HandlerSocket для запросов к MySQL не пробовали использовать?
  • 0
    А можете рассказать, что за проект описывается в статье?
  • 0
    Экономия должна быть экономной. Почему вы юзаете 1-у железку? 48Gb mem %) Я бы смотрел в сторону использования нескольких железяк, но с распаралелливанием нагрузки. За 50Е можно взять несколько VPS. Опять же — отказоустойчивость на 0. То есть весь ваш будущий рост будет только в наращивании мощности железа 1-ой машины, не логичнее ли планировать рост и масштабизацию нагрузки на кластер?
    • 0
      Потому что 1 железка — самый простой вариант. А параллелить — это сразу лоад балансер, код перепиливать и тд…
  • 0
    Пользуясь случаем спрошу — а у связки PHP+Mysql нет решения типа «пула коннектов»?
    Понимаю, что коннект к базе занимает миллисекунды, но может можно как-то оптимизировать.Зачем 36 млн. раз коннектиться к базе?
    • 0
      pconnect?
      • 0
        Да, но я тут по «правильному» работаю — через PDO…
        • 0
          PDO::ATTR_PERSISTENT => true
  • 0
    Вам огромное, огроменнейшее спасибо! А то, действительно, в сети миллионы статей про «хайлоад», но по сути везде крохи.
  • +1
    У нас всего 10 млн запросов в час на nginx, требующих достаточно непростой обработки через nginx + php-fpm, локальный кеш, глобальный кеш и api «мозгового центра», который достает нужную инфу после авторизации подключившегося клиента из базы.
    Специально потратил полтора часа времени и на практике проверил все предложенные вами настройки (за исключением бессмысленных вроде выставления значений по-умолчанию и безумных вроде max_children 4000). Проверял не просто так — реально надеялся, что кто-то где-то упустил и смогу снизить нарузку/необходимое количество инстансов. После каждой значимой манипуляции рестартовал испытуемый сервер и сравнивал показатели с другими. Увы, не смог добиться положительного статистически значимого результата, хотя рост количества обработанных запросов даже на 2% посчитал бы значимым.
    • 0
      opcache настроен надеюсь?
      Кстати автор в статье про него тоже ничего не сказал. Вообще никакие настройки PHP не показал.
      • 0
        естественно )
      • 0
        Без опкэша пока что работает. Это следующий шаг. Там вебморда на симфони, код пока пилили, разработчик попросил ни каких опкэшей не использовать, чтобы для начала все хорошо отладить.
        • 0
          ни каких опкэшей не использовать, чтобы для начала все хорошо отладить.

          А можно как-то этот момент аргументировать? Как по мне это кажется чуть более чем странным (учетом того что морда на симфони).
          • +1
            Разработчик очень странный у вас. Он кодит и тестирует прямо на продакшене? Т.к. если нет, то вообще смысл отключения opcache не понятен. Его включение снизит нагрузку примерно раза в 4.
  • 0
    Я довольно давно не писал на php, но с тех времен, когда писал, смутно помню, что там не было пула коннекшенов к базе. Возможно, сейчас это не так. Но если это так, то навскидку мне кажется, что эти установки соединений с базой будут сьедать ощутимое время проца.
    • +1
      у PHP есть persistent connection. То есть вместо того что бы закрывать соединение по окончанию обработки запроса, оно остается висеть и может быть реюзано следующим скриптом, который использует те же креденшелы. Естественно что это чуть сложнее, так как по окончанию работы скрипта вам обязательно надо завершить все транзакции и убрать локи, иначе это все перетянется на следующий запрос.
  • +1
    Такое ощущение, что настройки надерганы из разных мест наугад без «примерок».
    Хотя бы вот:
    my.cnf — open-files-limit = 150000
    limits.conf — * hard nofile 100000
    С nginx тоже самое.

    pm.max_requests = 0 помогает боротся с утечками памяти, в стороннем софте.
    Наоборот, мешает бороться с утечками, при том в своём коде — не рестартит fpm-процессы никогда (ну если шедуллер не решит их освободить и убить).

    И в итоге у вас там XtraDB или MyISAM? Что вы тюнили в конфиге mysql — совсем неясно.
    • 0
      InnoDb
      Про max_request я выше отвечал — было 500 пока боролись. Потом стало 0.
      Лимиты файлов — подрезал, а то излишне раздул. И не везде уменьшил, это есть немного, да.
  • 0
    Присоединюсь к вопросу про HandlerSocket. Все условия для такого перехода есть: MySQL, InnoDB, множество быстрых однотипных операций вставки. Почему бы нет?
    • 0
      Получил тут множество ценнейших советов. Будем копать во все стороны, в том числе и в эту.
  • 0
    Рекомендую попробовать поменять дефолтовый уровень изоляции мускуля с repeatable-read на read-commited. Может очень существенно поднять производительность (тут конечно все от задач). А вот держать на нуле innodb_flush_log_at_trx_commit — стремно имо. 2 хотя бы…
    Так же в зависимости от железок и системы с O_DIRECT. Может иметь обратный эффект.
  • +1
    Расскажите, а что будете делать, когда сервер ляжет?
    Сколько времени должен быть простой, чтобы стоимость второго сервера вылетела в трубу?

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