Как стать автором
Обновить

Комментарии 102

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


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


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

Ну pm.max_children = 4000 — это что-то невероятное. Если каждый процесс кушает по 20 мегабайт, то это уже 80 гигабайт памяти.
Расскажите еще про 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.

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

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

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

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

Если даже принять, что авторизация на сервер, а не в базу, то по вашим же словам на каждый запрос на веб-сервер приходится 2 запроса в БД. Соответсвенно, количество запросов к веб-серверу в 2 раза меньше. Правильно я понимаю?
НЛО прилетело и опубликовало эту надпись здесь
Не совсем. Клиент перед отправкой данных сначала запрашивает версию API на сервера, это ему nginx отдает, затем запрашивает контрольную сумму клиента на сервере, это ему так же nginx отдает.
Если что-то не совпало — автообновляется. А если совпало — то идет передача данных. Так что разница реальная по отношению к SQL запросам где-то меньше процентов на 20-30%.
А всякие nginx_mysql_module не рассматривались, раз в php почти ничего кроме запросов к базе и нет?
Да, это следующий шаг, сейчас изучаем как писать модули под nginx, думаем написать модуль и вообще уйти от php.
Node.js
У него HTTP-сервер по скорости сильно хромает.
Первый тест — Virtualbox, да и логике данное сранение не подается.
Второй тест — сранение вебсервера + PHP-FPM с вебсервером внутри nodejs
голую ноду в мир никто не выставляет, для этого есть nginx.
ReactPHP даст тот же профит примерно. Или PHP-PM. Полный отказ от прослойки дополнительной профита даст явно больше.
Скорее всего там не используются тяжелые фреймворки, да и фрейморки вообще, поэтому отличие будет небольшим. Но зато можно будет кэшировать prepared statements и set names.
Если вам только запросы в mysql, то достаточно самого nginx_mysql_module. Если все же какая-то логика, то почему бы не lua-nginx-module + lua-resty-mysql?
в секунду обрабатываются с загрузкой 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 значит совсем противоположное.
Ага, спасибо. Я давно отошел от админства, со времен freebsd 3-й мало что админил из юниксов. Пришлось вспоминать, изучать методом тыка.
По таймауту процессы и убиваются — сейчас у меня 30 секунд стоит.
Про max_request выше писал, прогнал, — было в конфиге 500 когда боролись с утечками. Как победили — стало 0, держит стабильно, память не исчезает в никуда.
Имхо на таком кол. запросов нужно логи вырубать, ну или не на HDD их сбрасывать.
Не знаю какие у вас там запросы, но если есть запросы, как вы сказали, только на запись, советую для этих запросов дополнительно глянуть сюда php.net/manual/en/function.fastcgi-finish-request.php это поможет немного легче жить nginx-у.
Ещё как вариант, возможно, имеет смысл посмотреть на модуля для того же nginx-а, которые умеют писать напрямую в DB, с пост обработкой на php.
О логах — я о логах запросов, а не ошибок.
Да, это решение мы на позапрошлой неделе сделали. Делаем финиш и дальше уже все работы по обработке данных и вставке их в БД.
И модуль да, хотим написать свой модуль под nginx чтобы уйти от php вообще.
Зачем тогда писать модуль для nginx-а, что ПОЛНОСТЬЮ уйти от php? Перепишите тогда полностью на на C++ и все…
Полностью уйти на приеме данных.
Веб-морда для пользователей остаётся, она вообще не symfony сделана.
Была идея по типу демона авторизации, что на java сделан, сделать и обработку входящих данных так же.
Но пока всё это под вопросом.
посмотрите тогда в сторону nginx + lua, возможно это то, что вам нужно
Зачем так много?
fastcgi_send_timeout 180s;
fastcgi_read_timeout 180s;
Когда подсчитывается статистика и удаляются записи устаревшие, в этот момент дорогой мускул лочит таблицу, и если в этот момент, ночной, кто-то юзает веб-морду сервиса — чуть чуть ждет и получает результат, вместо ошибки «что-то пошло не так на нашем сервисе».
Корявый момент, пока так его обошли.
Ночью делаем delete from where del_flag=1, а затем alter table чтобы уменьшить объем данных и облегчить процесс впихивания всей этой беды по максимуму в кэш InnoDb, а то база разрастается до 20 млн и выше записей, в каждой записей по 20 полей, и они активно используются в достаточно больших select-ах с несколькими джойнами, на веб-морде, и мускул начинает подтупливать.
Ночью делаем delete from where del_flag=1, а затем alter table чтобы уменьшить объем данных и облегчить процесс впихивания всей этой беды по максимуму в кэш InnoDb

Партиционирование таблиц не рассматривали? ALTER TABLE достаточно дорогая операция…
Пока не придумали как применить. Хотя слова эти витают в воздухе постоянно )
А как вы конкретно уменьшаете размер таблицы? Не выходит сделать так, чтобы лишние данные попадали в другую партицию?
Удаляем данные, делаем alter — оно дефрагментирует, так как InnoDb.
Спасибо!!!
Будем смотреть!
Если сильно сильно не повезет — потеряем пару секунд транзакций, для нас это не критично — данные от клиента прийдут заново очень быстро.
Из статьи не понятна структура таблиц/запросов. Но вероятнее всего под такой нагрузкой хранятся пары вида «ключ, значение».
Если так, то не лучше ли перейти на решения а-ля redis?
Если по каким-то причинам no-sql решения не подходят, а структура запросов INSERT одинакова гоните их в mysql через fifo pipe напрямую.
Вкратце — таблица пользователей, с их данными.
Вторая таблица, в которую идут основные инсерты — ИД юзера, плюс ИД записи данных этого юзера, и 20 полей данных этой записи.
У каждого юзера в среднем 18-20 записей данных.
Плюс вспомогательные таблицы для расчета некоторых полей, часть полей приходит от клиента, часть — калькулируется и затем вставляется в БД в виде записи окончательно.
Данные вносятся полагаю через 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.
Интересное решение. Что с безопасностью и фильтрацией вводимых данных напрямую в базу? $post_data2 = '1");\nDROP DATABASE;\n'; — ну так навскид :)
Код модуля смотрел очень давно, не помню. Но если обработки в нём и нет, то добавить её не так сложно.
Самим модулем пользовался на нагрузке около 100k per sec, падений не было.
Нет, данные идут из клиентов, сразу POST запрос в json формате по нужному урлу.
Логики вышеописанного это не меняет, запросы всё-равно попадут в ngx_http_form_input_post_read(ngx_http_request_t *r).
Только разбор JSON опять же придётся отдать на откуп этому модулю.
Спасибо, очень интересно! У нас есть идея разделить данные на те что надо дообрабатывать, всмысле сделать рассчеты по ним, и те что не надо, возможно это решение будет в тему для тех данных что нужно просто залить и сохранить.
Собственно, здесь можно обойтись и без пайпинга, а посмотреть в сторону github.com/openresty/drizzle-nginx-module#drizzle_query
Тут же для борьбы с инъекциями (о которых подымал вопрос aleks_raiden)можно использовать github.com/openresty/set-misc-nginx-module#set_quote_sql_str
> hhvm

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


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

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

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

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

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

И в итоге у вас там XtraDB или MyISAM? Что вы тюнили в конфиге mysql — совсем неясно.
InnoDb
Про max_request я выше отвечал — было 500 пока боролись. Потом стало 0.
Лимиты файлов — подрезал, а то излишне раздул. И не везде уменьшил, это есть немного, да.
Присоединюсь к вопросу про HandlerSocket. Все условия для такого перехода есть: MySQL, InnoDB, множество быстрых однотипных операций вставки. Почему бы нет?
Получил тут множество ценнейших советов. Будем копать во все стороны, в том числе и в эту.
Рекомендую попробовать поменять дефолтовый уровень изоляции мускуля с repeatable-read на read-commited. Может очень существенно поднять производительность (тут конечно все от задач). А вот держать на нуле innodb_flush_log_at_trx_commit — стремно имо. 2 хотя бы…
Так же в зависимости от железок и системы с O_DIRECT. Может иметь обратный эффект.
Расскажите, а что будете делать, когда сервер ляжет?
Сколько времени должен быть простой, чтобы стоимость второго сервера вылетела в трубу?
Столкнулся с интересным багом, упомянутые строки в файле /etc/security/limits.conf ломают вход пользователя root по ssh в Centos 7.5:
root soft nofile unlimited
root hard nofile unlimited
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории