Первое, что я заметил — множество советов как все настроить под большую нагрузку. Читайте их внимательно, обычно в тексте найдете, что речь про «высокую нагрузку» в 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 коннектов:
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
Тюнинг лимитов по файлам, так как ни каких юзеров на сервере нет, то особо не заморачиваемся:
* 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:
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_buffer_size 4K;
fastcgi_buffers 64 4k;
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 общаемся через сокеты:
fastcgi_send_timeout 180s;
fastcgi_read_timeout 180s;
Основное по конфигурации php-fpm:
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, ни каких билдов с сорцов с мегатюнингом не используется.
Думаю, кому-то будет полезна эта информация о настройках вся в одном месте.
Комментарии (101)
homm
14.07.2015 19:52+3Расскажите еще про pm.max_requests = 0. Это после каждого запроса интерпретатор перезапускается (т.е. всего 10000 раз в секунду)? Это же невозможно. Если наоборот, отключает перезагрузку интерпретатора, то как это «помогает бороться с утечками памяти»?
Ну pm.max_children = 4000 — это что-то невероятное. Если каждый процесс кушает по 20 мегабайт, то это уже 80 гигабайт памяти.blind_oracle
14.07.2015 20:23+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.
Так что у автора никакой борьбы с утечками нет :)RUnnerTomsk Автор
15.07.2015 07:43+1Ага, сейчас нет, тут прогнал маленько. Пока боролись — было было 500. Сейчас тьфу тьфу тьфу, все стало хорошо и поставил 0 в текущем конфиге.
RUnnerTomsk Автор
15.07.2015 07:354000 держит, но всё упирается вы этом случае в CPU, не справляется.
Сейчас хватает максимум 400 процессов.
doom369
14.07.2015 20:21+15Инфы мало. Что за запросы? Какой протокол? Какой средний размер запроса? Каждый ли реквест пишется в базу? Как в базу пишете? Один поток? Много потоков? Какого рода запросы? Инсерты? Инсерты с селектом? Еще что-то? Сколько данных в сек пишется на диск? Сколько читается? Какой трафифик на сервер туда-сюда? Сколько самих реквестов в базу? Как деплоймент делаете при такой нагрузке? Отрубаете сервер?
Вполне может оказатся что после ответов 10к рек-сек это мало для такого сервака =).FrostMoon
14.07.2015 21:53+5Подпишусь под вопросом.
Какова логика скриптов? Какие объемы данных? Какие индексы? Количество записей?
Мы все были бы очень признательны за чуть больший объем инфы.
RUnnerTomsk Автор
15.07.2015 07:42Запросы входят через http в json формате. В среднем приходит от 200 до 500 байт данных, парсится, и уже в нужном виде вставляется в БД.
Каждый пишется в БД, сначала идет select для проверки токена авторизации, если все ок — делается update для выставления флага удаления для потерявших актуальность записей, затем делается insert в БД с replace в случае если такой ключ уже имеется.
Соответственно есть еще один демон, на джаве, который висит на отдельном порту, слушает сокет, и клиент сначала в него кидает пару логин/пароль, получает токен авторизации, и уже с ним через http в json формате сливает данные на сервер.
Коряво, но когда-то было сделано такое решение, пока его не меняем.FrostMoon
15.07.2015 15:13Однажды на практике убедились, что если часть запросов из бекенда перенести на сторону nginx ( например модули на perl исполняемые nginx-ом) можно получить почти на порядок большее qps к БД. А лучше, наверное, модули на Lua. Правда все упирается в специфику данных и саму базу. Но мне кажется, вам стоит попробовать. Если интересно могу в личку скинуть наш пример. Нашей задачей был сбор статистических метрик с распаковкой данных и по возможности быстрой записью в БД.
Сижу на PgDay, пишу с телефона, прошу прощения за опечатки.
blind_oracle
14.07.2015 20:24+1А как же отказоустойчивость?
RUnnerTomsk Автор
15.07.2015 07:43+1Хромает. Думаем над этим. Пока не сбоит, но понятно что это не может быть вечным — один сервак у хетцера уже сожгли — винты вышли из строя.
mlogarithm
21.07.2015 11:38Винты — это болезнь хецнера, по опыту (парк в 30+ серверов в течении пары лет) — самая большая. Хотя у нас архитектура позволяла потерять несколько серверов, но в итоге пришли к тому, что заказывали новые винты и мониторили состояние работающих через smartctl. Это позволяло отловить умирающие винты заранее и принять превентивные меры.
AlexeyShurygin
14.07.2015 20:32+1«Итог — весь миллион клиентов обслуживается на одном сервере»
Учитывая что Хетцнер использует ненадежное железо десктоп класса, которое периодически падает — хороший выбор, успехов)
Касательно нагрузки — действительно 400 инсертов в секунду делаете на одном DB сервере?alexkrash
14.07.2015 22:22+1Честно говоря, 400 инсертов в секунду для одного сервера MySQL это ничто :) Другое дело, что тут всё на одной железяке.
neolink
14.07.2015 22:29+3а как вы посчитали что 400?! 10к запросов в секунду из которых 95% это инсерты, то есть 9500 инсертов же
RUnnerTomsk Автор
15.07.2015 08:0912k qps видел, выше — не попадал в тот момент на сервер ) Я редко на него захожу.
По железу — один сервак сожгли, винты посыпались, через 40 минут хетцер дал новый, переехали быстро.
Есть там и enterprise решения, но дорого для нас пока что.
inside22
14.07.2015 20:58+7Подскажите пожалуйста, а каким образом вы подсчитали количество запросов в час?
RUnnerTomsk Автор
15.07.2015 07:44+2mysql ведет статистику запросов. Берем количество, делим на время.
DoctorChaos
15.07.2015 11:15Так 36 млн. HTTP-запросов? Или в MySQL?
RUnnerTomsk Автор
15.07.2015 11:22В мускул. Но они все приходят через веб. Так что можно уровнять я думаю.
ffriend
15.07.2015 14:54+2Авторизация по каждому запросу, затем update в БД, затем insert данных с replace on duplicate
Если даже принять, что авторизация на сервер, а не в базу, то по вашим же словам на каждый запрос на веб-сервер приходится 2 запроса в БД. Соответсвенно, количество запросов к веб-серверу в 2 раза меньше. Правильно я понимаю?DoctorChaos
15.07.2015 20:09Кроме того, там могут выполняться SET NAMES и что-то еще. Тогда соотношение HTTP и SQL запросов будет еще меньше.
RUnnerTomsk Автор
20.07.2015 20:22Не совсем. Клиент перед отправкой данных сначала запрашивает версию API на сервера, это ему nginx отдает, затем запрашивает контрольную сумму клиента на сервере, это ему так же nginx отдает.
Если что-то не совпало — автообновляется. А если совпало — то идет передача данных. Так что разница реальная по отношению к SQL запросам где-то меньше процентов на 20-30%.
AterCattus
14.07.2015 23:54А всякие nginx_mysql_module не рассматривались, раз в php почти ничего кроме запросов к базе и нет?
RUnnerTomsk Автор
15.07.2015 07:45Да, это следующий шаг, сейчас изучаем как писать модули под nginx, думаем написать модуль и вообще уйти от php.
Roxis
15.07.2015 09:33-11Node.js
DLag
15.07.2015 10:07+2У него HTTP-сервер по скорости сильно хромает.
Roxis
15.07.2015 10:59+1Прошу пруфы. Сам не нашёл, только тесты подтверждающие обратное:
centminmod.com/siegebenchmarks/2013/020313
www.prahladyeri.com/blog/2014/06/php-fpm-vs-node-js-the-real-performance-battle.htmlDLag
15.07.2015 18:35Первый тест — Virtualbox, да и логике данное сранение не подается.
Второй тест — сранение вебсервера + PHP-FPM с вебсервером внутри nodejs
DLag
15.07.2015 18:39+1А раз вы любите синтетику, то вот вам примеры:
ufocoder.com/ru/blog/2015/benchmark-prostogo-http-servera-na-golang-i-nodejs#.VaPzF3uaCiA.twitter
gist.github.com/msoap/7060974
Fesor
15.07.2015 11:17ReactPHP даст тот же профит примерно. Или PHP-PM. Полный отказ от прослойки дополнительной профита даст явно больше.
DjOnline
15.07.2015 22:30Скорее всего там не используются тяжелые фреймворки, да и фрейморки вообще, поэтому отличие будет небольшим. Но зато можно будет кэшировать prepared statements и set names.
AterCattus
15.07.2015 15:15Если вам только запросы в mysql, то достаточно самого nginx_mysql_module. Если все же какая-то логика, то почему бы не lua-nginx-module + lua-resty-mysql?
aivus
15.07.2015 00:23+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 значит совсем противоположное.RUnnerTomsk Автор
15.07.2015 08:12Ага, спасибо. Я давно отошел от админства, со времен freebsd 3-й мало что админил из юниксов. Пришлось вспоминать, изучать методом тыка.
По таймауту процессы и убиваются — сейчас у меня 30 секунд стоит.
Про max_request выше писал, прогнал, — было в конфиге 500 когда боролись с утечками. Как победили — стало 0, держит стабильно, память не исчезает в никуда.
Rathil
15.07.2015 01:20+1Имхо на таком кол. запросов нужно логи вырубать, ну или не на HDD их сбрасывать.
Не знаю какие у вас там запросы, но если есть запросы, как вы сказали, только на запись, советую для этих запросов дополнительно глянуть сюда php.net/manual/en/function.fastcgi-finish-request.php это поможет немного легче жить nginx-у.
Ещё как вариант, возможно, имеет смысл посмотреть на модуля для того же nginx-а, которые умеют писать напрямую в DB, с пост обработкой на php.RUnnerTomsk Автор
15.07.2015 07:57Да, это решение мы на позапрошлой неделе сделали. Делаем финиш и дальше уже все работы по обработке данных и вставке их в БД.
И модуль да, хотим написать свой модуль под nginx чтобы уйти от php вообще.Rathil
15.07.2015 08:57Зачем тогда писать модуль для nginx-а, что ПОЛНОСТЬЮ уйти от php? Перепишите тогда полностью на на C++ и все…
RUnnerTomsk Автор
15.07.2015 09:01Полностью уйти на приеме данных.
Веб-морда для пользователей остаётся, она вообще не symfony сделана.
Была идея по типу демона авторизации, что на java сделан, сделать и обработку входящих данных так же.
Но пока всё это под вопросом.
AlexGx
15.07.2015 03:50+3Зачем так много?
fastcgi_send_timeout 180s;
fastcgi_read_timeout 180s;RUnnerTomsk Автор
15.07.2015 07:53Когда подсчитывается статистика и удаляются записи устаревшие, в этот момент дорогой мускул лочит таблицу, и если в этот момент, ночной, кто-то юзает веб-морду сервиса — чуть чуть ждет и получает результат, вместо ошибки «что-то пошло не так на нашем сервисе».
Корявый момент, пока так его обошли.
Ночью делаем delete from where del_flag=1, а затем alter table чтобы уменьшить объем данных и облегчить процесс впихивания всей этой беды по максимуму в кэш InnoDb, а то база разрастается до 20 млн и выше записей, в каждой записей по 20 полей, и они активно используются в достаточно больших select-ах с несколькими джойнами, на веб-морде, и мускул начинает подтупливать.FractalizeR
15.07.2015 11:03Ночью делаем delete from where del_flag=1, а затем alter table чтобы уменьшить объем данных и облегчить процесс впихивания всей этой беды по максимуму в кэш InnoDb
Партиционирование таблиц не рассматривали? ALTER TABLE достаточно дорогая операция…RUnnerTomsk Автор
15.07.2015 11:09Пока не придумали как применить. Хотя слова эти витают в воздухе постоянно )
FractalizeR
15.07.2015 11:45А как вы конкретно уменьшаете размер таблицы? Не выходит сделать так, чтобы лишние данные попадали в другую партицию?
RUnnerTomsk Автор
15.07.2015 12:23Удаляем данные, делаем alter — оно дефрагментирует, так как InnoDb.
script88
15.07.2015 11:49+1для ALTER TABLE на больших объемах, советую посмотреть в сторону www.percona.com/doc/percona-toolkit/2.2/pt-online-schema-change.html
TheRaven
15.07.2015 10:24+1innodb_flush_log_at_trx_commit=0
Аварийно сервер пробовали отключать и смотреть что с базой после этого?RUnnerTomsk Автор
15.07.2015 11:21Если сильно сильно не повезет — потеряем пару секунд транзакций, для нас это не критично — данные от клиента прийдут заново очень быстро.
simpleadmin
15.07.2015 10:44Из статьи не понятна структура таблиц/запросов. Но вероятнее всего под такой нагрузкой хранятся пары вида «ключ, значение».
Если так, то не лучше ли перейти на решения а-ля redis?
Если по каким-то причинам no-sql решения не подходят, а структура запросов INSERT одинакова гоните их в mysql через fifo pipe напрямую.RUnnerTomsk Автор
15.07.2015 11:07Вкратце — таблица пользователей, с их данными.
Вторая таблица, в которую идут основные инсерты — ИД юзера, плюс ИД записи данных этого юзера, и 20 полей данных этой записи.
У каждого юзера в среднем 18-20 записей данных.
Плюс вспомогательные таблицы для расчета некоторых полей, часть полей приходит от клиента, часть — калькулируется и затем вставляется в БД в виде записи окончательно.simpleadmin
15.07.2015 11:46Данные вносятся полагаю через 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.aleks_raiden
15.07.2015 11:51Интересное решение. Что с безопасностью и фильтрацией вводимых данных напрямую в базу? $post_data2 = '1");\nDROP DATABASE;\n'; — ну так навскид :)
simpleadmin
15.07.2015 11:56Код модуля смотрел очень давно, не помню. Но если обработки в нём и нет, то добавить её не так сложно.
Самим модулем пользовался на нагрузке около 100k per sec, падений не было.
RUnnerTomsk Автор
15.07.2015 12:20Нет, данные идут из клиентов, сразу POST запрос в json формате по нужному урлу.
simpleadmin
15.07.2015 12:34Логики вышеописанного это не меняет, запросы всё-равно попадут в ngx_http_form_input_post_read(ngx_http_request_t *r).
Только разбор JSON опять же придётся отдать на откуп этому модулю.RUnnerTomsk Автор
15.07.2015 12:36Спасибо, очень интересно! У нас есть идея разделить данные на те что надо дообрабатывать, всмысле сделать рассчеты по ним, и те что не надо, возможно это решение будет в тему для тех данных что нужно просто залить и сохранить.
simpleadmin
15.07.2015 12:54Собственно, здесь можно обойтись и без пайпинга, а посмотреть в сторону github.com/openresty/drizzle-nginx-module#drizzle_query
Тут же для борьбы с инъекциями (о которых подымал вопрос aleks_raiden)можно использовать github.com/openresty/set-misc-nginx-module#set_quote_sql_str
isden
15.07.2015 10:49> hhvm
По нашим тестам оно помогает заметно только при PHP <= 5.3, емнип.
На >= 5.5 лучше всего идет обычный PHP-FPM с OPCache.
onthefly
15.07.2015 12:23use epoll;
Nginx уже несколько лет как умеет самостоятельно выбирать оптимальный метод. Есть ли реальная необходимость в этой строке, чем руководствовались при её добавлении?xandr0s
15.07.2015 12:59Ну раз уж на то пошло,
worker_processes 8;
тут тоже можно заменить на auto где-то с 1.2 ветки. Но никто не запрещает их описывать. как и кучу других параметров, неплохо настроенных искаропки. Хотя бы, чтобы быть уверенным, что у тебя они верно выставлены.
ErgoZru
15.07.2015 12:40+1Вместо php-fpm компилировал и использовал hhvm одно время, действительно работает шикарно, значительно быстрее php-fpm, но есть беда — каждые 30-40 минут падает, причем наглухо.
Та же самая проблема, побороть так и не смог, хотя производительность мне понравилась.
Iforgot
15.07.2015 15:44Экономия должна быть экономной. Почему вы юзаете 1-у железку? 48Gb mem %) Я бы смотрел в сторону использования нескольких железяк, но с распаралелливанием нагрузки. За 50Е можно взять несколько VPS. Опять же — отказоустойчивость на 0. То есть весь ваш будущий рост будет только в наращивании мощности железа 1-ой машины, не логичнее ли планировать рост и масштабизацию нагрузки на кластер?
doom369
15.07.2015 15:51Потому что 1 железка — самый простой вариант. А параллелить — это сразу лоад балансер, код перепиливать и тд…
mihmig
15.07.2015 19:58Пользуясь случаем спрошу — а у связки PHP+Mysql нет решения типа «пула коннектов»?
Понимаю, что коннект к базе занимает миллисекунды, но может можно как-то оптимизировать.Зачем 36 млн. раз коннектиться к базе?
stychos
16.07.2015 00:03Вам огромное, огроменнейшее спасибо! А то, действительно, в сети миллионы статей про «хайлоад», но по сути везде крохи.
LeonidZ
16.07.2015 02:41+1У нас всего 10 млн запросов в час на nginx, требующих достаточно непростой обработки через nginx + php-fpm, локальный кеш, глобальный кеш и api «мозгового центра», который достает нужную инфу после авторизации подключившегося клиента из базы.
Специально потратил полтора часа времени и на практике проверил все предложенные вами настройки (за исключением бессмысленных вроде выставления значений по-умолчанию и безумных вроде max_children 4000). Проверял не просто так — реально надеялся, что кто-то где-то упустил и смогу снизить нарузку/необходимое количество инстансов. После каждой значимой манипуляции рестартовал испытуемый сервер и сравнивал показатели с другими. Увы, не смог добиться положительного статистически значимого результата, хотя рост количества обработанных запросов даже на 2% посчитал бы значимым.BupycNet
16.07.2015 08:55opcache настроен надеюсь?
Кстати автор в статье про него тоже ничего не сказал. Вообще никакие настройки PHP не показал.RUnnerTomsk Автор
20.07.2015 20:25Без опкэша пока что работает. Это следующий шаг. Там вебморда на симфони, код пока пилили, разработчик попросил ни каких опкэшей не использовать, чтобы для начала все хорошо отладить.
Fesor
21.07.2015 00:14ни каких опкэшей не использовать, чтобы для начала все хорошо отладить.
А можно как-то этот момент аргументировать? Как по мне это кажется чуть более чем странным (учетом того что морда на симфони).LeonidZ
21.07.2015 04:36+1Разработчик очень странный у вас. Он кодит и тестирует прямо на продакшене? Т.к. если нет, то вообще смысл отключения opcache не понятен. Его включение снизит нагрузку примерно раза в 4.
chabapok
16.07.2015 10:52Я довольно давно не писал на php, но с тех времен, когда писал, смутно помню, что там не было пула коннекшенов к базе. Возможно, сейчас это не так. Но если это так, то навскидку мне кажется, что эти установки соединений с базой будут сьедать ощутимое время проца.
Fesor
16.07.2015 11:18+1у PHP есть persistent connection. То есть вместо того что бы закрывать соединение по окончанию обработки запроса, оно остается висеть и может быть реюзано следующим скриптом, который использует те же креденшелы. Естественно что это чуть сложнее, так как по окончанию работы скрипта вам обязательно надо завершить все транзакции и убрать локи, иначе это все перетянется на следующий запрос.
inkvizitor68sl
16.07.2015 13:36+1Такое ощущение, что настройки надерганы из разных мест наугад без «примерок».
Хотя бы вот:
my.cnf — open-files-limit = 150000
limits.conf — * hard nofile 100000
С nginx тоже самое.
pm.max_requests = 0 помогает боротся с утечками памяти, в стороннем софте.
Наоборот, мешает бороться с утечками, при том в своём коде — не рестартит fpm-процессы никогда (ну если шедуллер не решит их освободить и убить).
И в итоге у вас там XtraDB или MyISAM? Что вы тюнили в конфиге mysql — совсем неясно.RUnnerTomsk Автор
20.07.2015 20:26InnoDb
Про max_request я выше отвечал — было 500 пока боролись. Потом стало 0.
Лимиты файлов — подрезал, а то излишне раздул. И не везде уменьшил, это есть немного, да.
yojick
18.07.2015 20:23Присоединюсь к вопросу про HandlerSocket. Все условия для такого перехода есть: MySQL, InnoDB, множество быстрых однотипных операций вставки. Почему бы нет?
RUnnerTomsk Автор
20.07.2015 20:27Получил тут множество ценнейших советов. Будем копать во все стороны, в том числе и в эту.
Garr
21.07.2015 17:12Рекомендую попробовать поменять дефолтовый уровень изоляции мускуля с repeatable-read на read-commited. Может очень существенно поднять производительность (тут конечно все от задач). А вот держать на нуле innodb_flush_log_at_trx_commit — стремно имо. 2 хотя бы…
Так же в зависимости от железок и системы с O_DIRECT. Может иметь обратный эффект.
datacompboy
12.08.2015 11:09+1Расскажите, а что будете делать, когда сервер ляжет?
Сколько времени должен быть простой, чтобы стоимость второго сервера вылетела в трубу?
youROCK
rusage каждого из запросов меньше 1мс? Из чего же состоят ваши скрипты? Просто mysql_query без авторизации?
RUnnerTomsk Автор
Авторизация по каждому запросу, затем update в БД, затем insert данных с replace on duplicate
gandjustas
БД от такого количества запросов не падает? Одно дело принять по сети 10К запросов в секунду, другое дело на диск их записать.
doom369
Это в джейсоне. То есть самих данных <200 байт в запросе. 10к рек-сек * 200 байт == 2МБ сек — скорость записи на диск. То есть даже любой самый хиленький hdd справится.
neolink
это если индексов нет
RUnnerTomsk Автор
5-ть индексов, 3 по одному полю, один по двум полям, один по трем полям.
BupycNet
Мне кажется, или в случае чего, можно ведь в redis эти однородные инсерты запихивать, и раз в несколько секунд делать один большой запрос к БД на обработку.
doom369
Если в редис, то оперативка закончится за 1 день
aleks_raiden
Можно заюзать ssdb, и писать с той же скоростью и тем же протоколом, что и редис, но пока есть место на диске (я пробовал на уровне 40+Гб где-то)
doom369
А можно и MySQL, что и сделал автор. Зачем ssdb? Зачем редис?