Распределенная блокировка, реализованная автономным Redis
1. Скрипт для реализации распределенных блокировок на одной машине (рекомендуемая официальная реализация)
SET lock_key random_value NX PX 10000
// do sth
eval "if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end"
2. Вопросы, требующие внимания (управление снятием блокировки и контроль тайм-аута блокировки). Случайное_значение должно быть гарантировано уникальным, и для его обеспечения можно использовать trace_id!
3. Есть проблемы. Redis на одной машине зависит только от одного Redis. Когда зависимый Redis зависает, это вызовет относительно большие проблемы!
4. Можно ли гарантировать развертывание Redis в режиме master-slave? Основная причина заключается в том, что синхронизация данных между ведущим и подчиненным узлами Redis является асинхронной.
Распределенные блокировки, реализованные распределенным Redis
Алгоритм Редлока
Алгоритм Redlock основан на N полностью независимых узлах Redis (обычно N может быть установлено равным 5).
1. Получить текущее время (в миллисекундах).
2. Последовательно выполните операцию получения блокировки для N узлов Redis. Эта операция получения аналогична процессу получения блокировок на основе одного узла Redis. Для того, чтобы алгоритм мог продолжать работать, когда определенный узел Redis недоступен, операция получения блокировки также имеет тайм-аут (time out), который намного меньше эффективного времени блокировки (десятки миллисекунд). . Клиенту не удалось получить блокировку от узла Redis (Например, узел Redis недоступен или блокировка узла Redis уже удерживается другими клиентами.), следует немедленно попробовать следующий узел Redis.
3. Подсчитайте, сколько времени занимает весь процесс получения блокировки.Метод расчета заключается в вычитании времени, записанного на шаге 1, из текущего времени. Если клиент успешно получает блокировку с большинства узлов Redis (>= N/2+1), а общее время, затраченное на получение блокировки, не превышает времени действия блокировки, то клиент считает окончательное получение блокировки Блокировка прошла успешно; в противном случае , считается, что окончательная блокировка не удалась.
4. Если блокировка, наконец, получена успешно, то время действия блокировки должно быть пересчитано, что равно времени действия исходной блокировки за вычетом времени, затраченного на получение блокировки, рассчитанного на шаге 3.
5. Если окончательное получение блокировки не удалось (Это может быть связано с тем, что количество узлов Redis, которые получают блокировку, меньше, чем N/2+1, или весь процесс получения блокировки занимает больше времени, чем начальное время действия блокировки.), то клиент должен немедленно инициировать операцию снятия блокировки для всех узлов Redis (здесь, чтобы убедиться, что все узлы Redis могут сообщить об освобождении полученной блокировки).
Алгоритм Redlock реализован в предположении, что разные машины имеют одинаковые часы или что ошибка мала и незначительна. (Это тоже точка, распыленная автором DDIA)
Существующие проблемы:
1. О стойкости Redis
Предположим, всего имеется 5 узлов Redis: A, B, C, D, E. Предположим, что происходит следующая последовательность событий:
1.1 Клиент 1 успешно блокирует A, B, C и успешно получает блокировку (но D и E не заблокированы).
1.2 Узел C аварийно завершает работу и перезапускается, но блокировка, добавленная клиентом 1 на C, не сохраняется и теряется.
1.3 После перезапуска узла C клиент 2 блокирует C, D и E, и блокировка успешно получена.
Решение, данное Redis: отложенный перезапуск, то есть когда узел Redis зависает, не перезапускать сразу, а дождаться истечения времени блокировки перед перезапуском.
2. Как снять блокировку, если получение блокировки занимает слишком много времени для выполнения последующих операций? Лично я считаю, что деловая сторона должна определить время, необходимое для коммерческой операции.
3. Когда автор Redis проектировал Redlock, он полностью учёл влияние сетевой задержки и паузы программы. Однако что касается задержки между клиентом и сервером ресурсов (то есть задержки, возникающей после шага 3 алгоритма), он признает, что все реализации распределенных блокировок, включая Redlock, не имеют хорошего способа справиться с ней.
Мнение автора DDIA о Redlock
В этой статье Мартин разделяет использование замков на два типа:
- Для эффективности координируйте работу каждого клиента, чтобы избежать дублирования работы. Даже если замок время от времени выходит из строя, можно лишь повторно выполнить некоторые операции, и никаких других неблагоприятных последствий не произойдет. Например, повторная отправка одного и того же электронного письма.
- Для правильности. Аннулирование блокировки не допускается ни при каких обстоятельствах, потому что однажды это может означать несогласованность данных, потерю данных, повреждение файла или другие серьезные проблемы.
1. Распределенные блокировки с функцией автоматического истечения срока действия должны обеспечивать какой-либо механизм ограждения, чтобы обеспечить настоящую защиту от взаимного исключения общих ресурсов. Redlock не предоставляет такого механизма.
На приведенном выше рисунке показана проблема доступа к общим ресурсам несколькими клиентами из-за пауз GC. Причина в том, что клиент ждет истечения времени блокировки во время паузы GC и освобождается, но клиент этого не знает и думает, что он все еще находится в состоянии удержания блокировки. На приведенном выше рисунке показано использование токенов ограждения для решения проблемы сбоя блокировки и невыпуска. Redis указывает на уязвимость на рисунке выше: если и у клиента 1, и у клиента 2 есть пауза GC, два токена ограждения задерживаются, и они поступают на ресурсный сервер почти одновременно, но сохраняют порядок, то ресурс сервер не будет проверять, есть ли проблема? На данный момент есть ли конфликт в доступе к ресурсу?2. Redlock построен на модели незащищенной системы. Это налагает строгие требования на временные допущения системы, которые не могут быть гарантированы в реальных системах.
Алгоритм Redlock в значительной степени зависит от машинных часов.Мартин считает, что реализация алгоритма не должна делать никаких предположений о времени: процессы могут быть приостановлены на любой период времени, пакеты могут произвольно задерживаться в сети, а часы могут быть произвольными. Несмотря на это, алгоритм по-прежнему выполняется правильно и дает правильные результаты.
По поводу ненадежности часов: Автор Redis считает, что требования Redlock к часам не обязательно должны быть абсолютно точными, нужно лишь, чтобы часы были почти точными.
3. Сетевая задержка после завершения третьего шага Redlock также вызывает отказ алгоритма Redlock, но эта проблема не уникальна для алгоритма Redlock.
Мартин пришел к следующим выводам:
- Если для повышения эффективности используются распределенные блокировки, допускающие случайные сбои блокировки, то схема блокировки одного узла Redis является достаточной, простой и эффективной. Redlock — тяжелая реализация.
- Если вы используете распределенные блокировки для корректности в серьезных ситуациях, не используйте Redlock. Это недостаточно сильный алгоритм, основанный на асинхронной модели, и его предположения о модели системы содержат много опасных компонентов (для тайминга). Кроме того, у него нет механизма предоставления токенов ограждения. Итак, какую технологию следует использовать? Мартин считает, что следует рассмотреть что-то вроде Zookeeper или базы данных, поддерживающей транзакции.
окончательное обсуждение
Поняв логику алгоритма Redlock и его возможные проблемы, мы можем выбирать для наших собственных бизнес-сценариев~