Фраза
"Обычная БД вроде MySQL не справляется с таким потоком запросов и не может так быстро отвечать."
Как раз имела в виду, что делать такое число UPDATE'ов в MySQL очень тяжело.
"У нас сложная бизнес логика. Почти всё селекты, да." Вот тут и выходит. Селекты делать не проблема. Хотя проблема начинается, когда идет 1000 UPDATE'ов и селекты одновременно связанные с обновляемыми строками — а они все лочатся.
Собственно поэтому мы будем частично использовать Redis + еще использовать его как буфер. Т.е. набрали за пару секунд данных, а потом уже сбросили это в БД одиночным запросом.
Насчет тысяч и десятков тысяч — скорее всего у вас на эти 5000 запросов, всего 100-200 UPDATE/INSERT в секунду. Так что да — нам нужно те же 5000 — только UPDATE запросов.
5740 запросов в секунду.
А тестах выше у нас миллион за 35 секунд. Т.е. 28 тысяч в секунду.
Теперь интересно, какое у вас железо? У нас вот 28 тысяч в секунду на одном ядре Intel Xeon E5-1650v2 3.5 Ghz обрабатывается. И еще деталь — 28 тысяч — инкрементов в секунду.
И самая важная деталь — это 28 тысяч соединений в секунду.
Я немного модифицировал бенчмарк, сделал 100 потоков по 10000 инкрементов через 100 соединений. И о чудо миллион инкрементов за 10 секунд.
100 тысяч инкрементов за 1 секунду. В 20 раз больше инкрементов, чем у вас запросов в секунду, на одном ядре. При этом нагрузка не сильно большая, все внутри оперативки работает.
MySQL при таком количестве селектов (5000) в секунду, скорее всего ест несколько ядер.
А теперь я еще модифицировал скрипт. Вот он теперь делает один SET и миллион GETов (типичные SELECT'ы для MySQL) и о чудо — за 3 секунды миллион GET'ов. Т.е. 333 тысячи селектов за секунду на одном ядре. В 60 раз быстрее.
И теперь на секундочку — сколько у вас в секунду идет UPDATE и INSERT? У нас проблема именно в том, что к нам идут несколько тысяч ответных запросов на установление статуса уведомления у определенного пользователя, можете ли вы обработать 5000 инсертов, а потом еще 5000 обновление полей? И так, чтобы не спавнить 5000 форков PHP-FPM, т.е. нужно обрабатывать запросы быстрее чем за 10-20 мс, иначе слишком много процессов скопится.
В итоге пришли к тому что я и имел в виду — в данной вариации, если работать через php с сокетом, то существует вероятность, что буфер заполнятся и скрипт упадет.
Можно конечно переписать этот драйвер, но через tcp все работает без проблем и разница с pconnect незначительна.
Этот код работает начиная с php 5.5 где то.
Насчет комментария — описал же, что не помогло это.
Причем с pconnect все работает.
Насчет нагрузок — сомневаюсь что вы писали в редис одновременно в 1000 потоков. Тут вероятно, что сам redis не справляется через UNIX сокет т.к. слишком быстро идут запросы, а TCP из за доп оверхеда соединения уменьшает нагрузку на сам redis.
Чтобы писать в 1000 потоков одновременно (сами смотрите, это было миллион за 4 секунды) вам нужно делать 250 тысяч запросов в секунду. При этом бенчмарк редиса (redis-benchmark) у меня лично показывает где то 100 тысяч запросов в секунду.
То есть — чтобы достичь тех же нагрузок — вам нужно редис на 100 загрузить.
У меня на тестах редис показывал 100% потребления одного ядра. То есть по сути эти был его предел по обработке.
Но все таки тот факт, что TCP в случае нагрузки замедляется, а в UNIX socket просто отваливается — мне кажется первый вариант лучше.
Я же говорю — сам увидился. Конечно как вариант, может php-redis вообще сразу передает управление дальше, т.е. я отправляю incr, после чего у меня идет закрытие соединения, а вот сам модуль php-redis возможно в этот момент все это отправляет.
Но вообще я сильно сомневаюсь в этом.
Добавил еще в статью пару тестов. Обнаружил, что Redis как то подцепляет pconnect и работает с ним в 1.5-2 раза быстрее!
При этом разница между tcp и unix сокетами при этом составляет 5-10%.
Увеличил лимиты еще, поставил max_clients (кстати с tcp и так работало )
Насчет close — даже если его не писать — все равно при завершении дочернего процесса же срабатывает деструктор. Т.е. соединение закрывается.
Итог таков что даже увеличив лимиты и т.д. — все равно UNIX сокет не работает при таком количестве запросов, а tcp работает.
Не совсем правы. Поставил миллион. Попробовал — все равно ошибки.
Потом вспомнил — тут 1000 процессов делают по 1000 соединений в цикле. Одновременно может быть не более 1000 соединений. (Каждый процесс делает одновременно лишь один коннект а потом дисконнект)
При этом лимит 100 тысяч.
Так что лимит тут не виноват, а что то другое.
Да лимиты убраны. 3 секунды это скорее проблема редиса. То есть форки отдали на сервер редиса incr, потом завершили работу, потом родительский процесс об этом узнал (что все закончили работу), после чего делает еще одно соединение с редисом и запрашивает количество.
Как итог — нужно время чтобы оно успело обработаться.
Тут скорее дело в том, что тот же MySQL блокирует записи на чтение при UPDATE, редис же так не делает, поэтому нужно учитывать, что после завершения работы в фоне множества процессов, сразу получить самые свежие данные не получится.
"Все эти подписки выгодны только бизнесу, но никак не человеку. Лучше один раз заплатить за телефон $500, чем платить за него по $20 в месяц в течении всего срока пользования (допустим пяти лет)."
А теперь представьте — я буду платить 20$-30$ в месяц и менять телефон каждый год, или пол года. В итоге я буду в плюсе. Технически мой Б/У можно "восстановить" и потом продать на 10-20% от стоимости. Или дать тем, кто будет платить меньше в месяц чем я.
К слову почему-то в статье не указано что в сети tor у флибусты открыто больше книг — именно вся эта база и доступна через телеграм.
На обычном же сайте к нимс просто нет доступа
Да так и есть. Отправка пушей собственно состоит в том чтобы создать записи в БД и отправить в сторонние API команды. В gcm, apns, telegram отправка почты через SMTP и т.д.
SocketPush у нас тоже кстати на php сделан, как и парсер RSS и система очередей для отправки отложенных писем. Для параллельности используются форки.
"Обычная БД вроде MySQL не справляется с таким потоком запросов и не может так быстро отвечать."
Как раз имела в виду, что делать такое число UPDATE'ов в MySQL очень тяжело.
"У нас сложная бизнес логика. Почти всё селекты, да." Вот тут и выходит. Селекты делать не проблема. Хотя проблема начинается, когда идет 1000 UPDATE'ов и селекты одновременно связанные с обновляемыми строками — а они все лочатся.
Собственно поэтому мы будем частично использовать Redis + еще использовать его как буфер. Т.е. набрали за пару секунд данных, а потом уже сбросили это в БД одиночным запросом.
Насчет тысяч и десятков тысяч — скорее всего у вас на эти 5000 запросов, всего 100-200 UPDATE/INSERT в секунду. Так что да — нам нужно те же 5000 — только UPDATE запросов.
А тестах выше у нас миллион за 35 секунд. Т.е. 28 тысяч в секунду.
Теперь интересно, какое у вас железо? У нас вот 28 тысяч в секунду на одном ядре Intel Xeon E5-1650v2 3.5 Ghz обрабатывается. И еще деталь — 28 тысяч — инкрементов в секунду.
И самая важная деталь — это 28 тысяч соединений в секунду.
Я немного модифицировал бенчмарк, сделал 100 потоков по 10000 инкрементов через 100 соединений. И о чудо миллион инкрементов за 10 секунд.
100 тысяч инкрементов за 1 секунду. В 20 раз больше инкрементов, чем у вас запросов в секунду, на одном ядре. При этом нагрузка не сильно большая, все внутри оперативки работает.
MySQL при таком количестве селектов (5000) в секунду, скорее всего ест несколько ядер.
А теперь я еще модифицировал скрипт. Вот он теперь делает один SET и миллион GETов (типичные SELECT'ы для MySQL) и о чудо — за 3 секунды миллион GET'ов. Т.е. 333 тысячи селектов за секунду на одном ядре. В 60 раз быстрее.
И теперь на секундочку — сколько у вас в секунду идет UPDATE и INSERT? У нас проблема именно в том, что к нам идут несколько тысяч ответных запросов на установление статуса уведомления у определенного пользователя, можете ли вы обработать 5000 инсертов, а потом еще 5000 обновление полей? И так, чтобы не спавнить 5000 форков PHP-FPM, т.е. нужно обрабатывать запросы быстрее чем за 10-20 мс, иначе слишком много процессов скопится.
Можно конечно переписать этот драйвер, но через tcp все работает без проблем и разница с pconnect незначительна.
Насчет комментария — описал же, что не помогло это.
Причем с pconnect все работает.
Насчет нагрузок — сомневаюсь что вы писали в редис одновременно в 1000 потоков. Тут вероятно, что сам redis не справляется через UNIX сокет т.к. слишком быстро идут запросы, а TCP из за доп оверхеда соединения уменьшает нагрузку на сам redis.
Чтобы писать в 1000 потоков одновременно (сами смотрите, это было миллион за 4 секунды) вам нужно делать 250 тысяч запросов в секунду. При этом бенчмарк редиса (redis-benchmark) у меня лично показывает где то 100 тысяч запросов в секунду.
То есть — чтобы достичь тех же нагрузок — вам нужно редис на 100 загрузить.
У меня на тестах редис показывал 100% потребления одного ядра. То есть по сути эти был его предел по обработке.
Но все таки тот факт, что TCP в случае нагрузки замедляется, а в UNIX socket просто отваливается — мне кажется первый вариант лучше.
Но вообще я сильно сомневаюсь в этом.
При этом разница между tcp и unix сокетами при этом составляет 5-10%.
Насчет close — даже если его не писать — все равно при завершении дочернего процесса же срабатывает деструктор. Т.е. соединение закрывается.
Итог таков что даже увеличив лимиты и т.д. — все равно UNIX сокет не работает при таком количестве запросов, а tcp работает.
Потом вспомнил — тут 1000 процессов делают по 1000 соединений в цикле. Одновременно может быть не более 1000 соединений. (Каждый процесс делает одновременно лишь один коннект а потом дисконнект)
При этом лимит 100 тысяч.
Так что лимит тут не виноват, а что то другое.
Redis 3.0.7
Добавил в статью код.
Как итог — нужно время чтобы оно успело обработаться.
Тут скорее дело в том, что тот же MySQL блокирует записи на чтение при UPDATE, редис же так не делает, поэтому нужно учитывать, что после завершения работы в фоне множества процессов, сразу получить самые свежие данные не получится.
А теперь представьте — я буду платить 20$-30$ в месяц и менять телефон каждый год, или пол года. В итоге я буду в плюсе. Технически мой Б/У можно "восстановить" и потом продать на 10-20% от стоимости. Или дать тем, кто будет платить меньше в месяц чем я.
На обычном же сайте к нимс просто нет доступа
SocketPush у нас тоже кстати на php сделан, как и парсер RSS и система очередей для отправки отложенных писем. Для параллельности используются форки.