Comments
Мы пользуемся Sorted Sets для таблицы рейтинга в ААА игре (миллион одновременных игроков). Придумали много решений что делать если Redis не справится, упадёт и прочее, но пока 5 лет стоит и прекрасно справляется.
Рецепты скорее вредные, чем полезные. Я понимаю, что они по сути все взяты с сайта документации Redis или с Redis Labs и предложены там как каноничные паттерны, но в результате просто получается скверная реализация.

Очередь ещё более-менее, хотя и без подтверждений. В самой документации к Redis описано, как делать правильно: redis.io/commands/rpoplpush#pattern-reliable-queue

Мьютексы тоже странные: лок нужно получать в цикле, это крайне неэффективно. Сам метод тоже предложен в документации к Redis: https://redis.io/commands/set#patterns. Однако там же рекомендуют не изобратать велосипед, а использовать готовые реализации локов на редисе для различных языков. Но лучше что-то ещё поискать, редис для этого очень слабо подходит.

Rate limiter взят вот отсюда: https://redislabs.com/redis-best-practices/basic-rate-limiting/. Проблема с ним в том, что это никакой не рэйтлимитер, это буквально ограничение числа запросов в какой-то определённый временной интервал, а не ограничение частоты запросов. К примеру, все запросы могут придти в первые секунды временного отрезка и все они будут взяты в работу, а оставшееся время бэкенд будет сидеть без дела. А потом, в следующий временной интервал, бэкенд снова будет готов вычерпать весь лимит в первые мгновения.

То есть, он просто делает не то, что нужно. Правильная реализация должна использовать что-нибудь вроде алгоритма leaky bucket. В nginx это реализовано корректно, лучше пользоваться им для этих целей.
Представленные реализации, скорее некий Proof of Concept, на основе которого можно допилить ту или иную реализацию по своим требованиям.

Касаемо лока в цикле — это стандартный механизм retry. Если посмотреть на внутренности готовых реализаций по Вашей ссылке, там будет ретрай лока также выставляться в цикле.

По поводу rate limiter так же может быть большее количество различных реализаций, в моем посте представлен базовый вариант, который решает большую часть кейсов. Если же вам нужно что-то более кастомное и сложное, с равномерным распределением нагрузки — можно реализовать более сложные алгоритмы.
Представленные реализации, скорее некий Proof of Concept, на основе которого можно допилить ту или иную реализацию по своим требованиям.
Proof of Concept из документации? Звучит инновационненько.

Касаемо лока в цикле — это стандартный механизм retry.
Стандартный для чего? Наличие активного опроса по сети в приложении — существенный недостаток и что угодно, но точно не стандарт. И для взятия локов в частности. Посмотрите, как выглядит взятие распределённого лока с использованием Consul, например.

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

Поэтому я пишу:
Но лучше что-то ещё поискать, редис для этого очень слабо подходит.
Даже если Вы на чём Вам удобно напишете сетевой демон, который будет принимать две команды lock и unlock, и при этом не отправлять ответ на lock пока он уже не взят, то это будет уже существенно лучше, чем то решение на редисе. На чём-то однопоточном и асинхронном это реализовать тривиально. Но лучше использовать что-то наподобие консула.

По поводу rate limiter так же может быть большее количество различных реализаций, в моем посте представлен базовый вариант, который решает большую часть кейсов. Если же вам нужно что-то более кастомное и сложное, с равномерным распределением нагрузки — можно реализовать более сложные алгоритмы.
Что это за рэйтлимит, если он не имеет этой самой функции разравнивания мгновенной частоты запросов? Какой такой кейс он решает, каких «большинство»?

Бывают реализации хорошие, бывают не очень. Об этом и был весь мой пассаж с предложением лучших готовых решений.
Какой такой кейс он решает

Ну вот я на одном из своих сайтов примерно похожей реализацией ограничил частоту комментирования постов. Главное — защититься от лютых флуд/спам-атак и обиженных срачующихся пользователей, поставив лимит типа N комментов в час, а исчерпают этот лимит за час или за 5 минут активной дискуссии — уже не так важно, главное чтобы чрезмерный флуд в принципе был остановлен. Городить leaky bucket здесь в общем-то ни к чему.

Посмотрите, как выглядит взятие распределённого лока с использованием Consul, например.

Но лучше использовать что-то наподобие консула.

У вас есть опыт использования DLM Консула на PHP? Я попробовал и выглядит как-то не очень: медленно, нестабильно.
Про кеширование тоже не очень хорошо описано, точнее, сложно очень.

redis хорош в плане кеширования тем, что его можно настроить так, чтобы не нужно было явно задавать ttl ключам. Самый лучший код когда его нет.

Нужно настроить maxmemory, maxmemory-policy (allkeys-lru например) + отключаем save на диск. В добавок в приложении «заводим» логику такую:
— в кеше у нас будет два namespace ключей, каждое именуется как ревизия кода
— при деплое кода ревизия меняется, соответственно начинаем писать в новый namespace, постепенно старый «вытесняется» за счет maxmemory-policy
Почему так: как известно, redis однопоточный, и если у вас maxmemory в 16-32Gb, то сброс кеша после деплоя приложения может привести к тому, что приложение будет недоступно в течение нескольких секунд, потому что редис пока не выполнит команду, за другую не возьмется.

Получаем прекрасный работающий кеш с минимум кода!

redis.io/topics/lru-cache
если у вас такие большие нагрузки и кол-во юзеров
не думали использовать, или вдруг уже, Redis для хранения сессий, или есть какие-либо ограничения для такого «хода»?
В качестве основной БД у нас используется Postgres, и сессии хранятся в нем, но сессии кэшируются в Redis и соответственно Redis выступает в качестве горячего хранилища.
Redis очень удобный инструмент, но его нужно правильно и аккуратно использовать. Шаблон Mutex стандартный, но все немного сложнее. Что произойдет, когда $this->doSomeLogick() упадет? Да, обработка исключения или finally конечно нужна, но это не решение проблемы, т.к. падать может весь процесс/виртуальная машина, вся железка. А редис стоит у вас отдельно ведь, и блокировка у него повиснет до прихода сисадмина, чего бы хотелось избежать.

В примере Rate Limiter стоит задать время жизни ключа.
Mutex в текущей реализации имеет свой TTL, соответственно при падении процесса лок снимается, как только истечет TTL, не обязательно ждать прихода сисадмина.

По поводу Rate Limit, спасибо за замечание, там действительно нужно выставить TTL ключу через команду EXPIRE, чтобы не накапливать неактуальные ключи.
Да, про TTL я просмотрел, он есть, но это не полное решение проблемы :) Для чего делается Mutex? Во время операции защищенной Mutex по какой-то причине доступ разрешен только одному процессу, при сбросе лока по таймауту, следующий процесс, которому будет выдан этот mutex встретит систему в промежуточном состоянии, причем в вашем решении он даже это не сумеет обнаружить.
Т.е. смысл mutex теряется.

Для взрослых же применений есть еще одна проблема — у вас все блокировки находятся у одного инстанса redis'а и проблемы с ним — это большие проблемы со всем сервисом. На эту тему было изрядно копий сломано, хотя разработчики считают, что 5 инстансов должно хватить, если я правильно помню результат той дискуссии :)
то 5 инстансов должно хватить, если я правильно помню результат той дискуссии :)

>= 3 инстансов и кворум локов на них должен быть. Именно так работает RedLock.
Но это «медленно» и «а зачем». :)
Наш небольшой кластер Redis серверов обрабатывает около 1 миллиона запросов в секунду.


Расскажите, пожалуйста, про топологию кластера (sentinel?), и железо, которое вы используете тут. И еще вопрос — речь про один кластер, или про группу кластеров (шардов)?
Only those users with full accounts are able to leave comments. Log in, please.