Как создать распределенный замок с Redis?
В документации redis я обнаружил, что примитивная блокировка может быть реализована через SETNX:
http://redis.io/commands/setnx
-
C4 отправляет SETNX lock.foo, чтобы получить блокировку
-
Аварийный клиент C3 все еще удерживает его, поэтому Redis ответит 0 на C4.
-
C4 отправляет GET lock.foo, чтобы проверить, истек ли срок блокировки. Если это не так, он будет спать некоторое время и повторить попытку с самого начала.
-
Вместо этого, если блокировка истекла, потому что время Unix в lock.foo больше, чем текущее время Unix, C4 пытается выполнить:
GETSET lock.foo
-
Из-за семантики GETSET C4 может проверить, сохраняется ли старое значение, хранящееся в ключе, истекшую временную метку. Если это так, замок был приобретен.
-
Если другой клиент, например C5, был быстрее, чем C4, и приобрел блокировку с помощью операции GETSET, операция C4 GETSET вернет отметку с истекшим сроком действия. C4 просто перезапустится с первого шага. Обратите внимание, что даже если C4 несколько раз установит ключ в несколько секунд, это не проблема.
Однако, как отмечали некоторые пользователи, использование временной отметки UNIX в качестве срока действия требует, чтобы время клиента и сервера было полностью синхронизировано. Есть ли лучшая альтернатива для создания глобальной/распределенной блокировки в Redis?
Ответы
Ответ 1
Используйте SET
вместо SETNX
. SET
принимает аргументы для времени истечения в секундах и миллисекундах вместо значения метки времени UNIX.
Старый шаблон на основе SETNX документируется только по историческим причинам.
Из SETNX
описание:
ПРИМЕЧАНИЕ. Начиная с Redis 2.6.12, можно создать много более простой блокирующий примитив с использованием команды SET для получения блокировки, и простой Lua script, чтобы освободить замок. Шаблон документирован на странице команд SET.
Ответ 2
Используя redis >= 2.6, решение LUA script было бы замечательным. Lua script всегда выполняется атомарно так:
--lockscript, parameters: lock_key, lock_timeout
local lock = redis.call('get', KEYS[1])
if not lock then
return redis.call('setex', KEYS[1], ARGV[1], "locked");
end
return false
Другое решение, основанное на новых параметрах команды SET
SET lock_key "locked" EX lock_timeout NX
Использование redis < 2.6 можно использовать шаблон с несколькими:
MULTI
SETNX tmp_unique_lock some_value
EXPIRE tmp_unique_lock
RENAMENX tmp_unique_lock real_lock
EXEC
Ответ 3
перейдите по ссылке
хороший проект, объясняющий блокировку
http://redis.io/topics/distlock
https://github.com/mrniko/redisson
Ответ 4
Для установки блокировки достаточно новых аргументов для SET, но они работают только на Redis >= v2.6.12, вам также нужно подумать о том, как блокировка будет отменена и истечет и т.д.
Я написал сообщение в нашем блоге Engineering о распределенных блокировках с использованием Redis. Он охватывает сценарии того, как надежно установить и отпустить блокировку, с проверкой и предотвращением блокировки. Я также включаю модуль, написанный в Node.js, который вы можете использовать для блокировки прямо из коробки.
Ответ 5
Я придумал решение SET EX NX
, которое упоминалось в крутом драгоценном камне - simple_redis_lock
Код прост и выглядит следующим образом:
def lock(key, timeout)
if @redis.set(key, Time.now, nx: true, px: timeout)
begin
yield
ensure
release key
end
end
end
Ответ 6
Хорошая статья о замках с Redis: " Как сделать распределенную блокировку.
Я думаю, что алгоритм Redlock - это плохой выбор, потому что он "ни рыба, ни птица": он излишне тяжелый и дорогой для блокировок оптимизации эффективности, но он недостаточно безопасен для ситуаций, в которых правильность зависит от блокировки.
В частности, алгоритм делает опасные предположения о временных и системных часах (по существу, предполагая синхронную систему с ограниченной сетевой задержкой и ограниченным временем выполнения для операций), и это нарушает свойства безопасности, если эти предположения не выполняются. Кроме того, в нем отсутствует средство для создания маркеров фехтования (которые защищают систему от длительных задержек в сети или в приостановленных процессах).
Taooka исправить все описанные проблемы. Он изолирует сломанных клиентов, и никто не может получить блокировку до тех пор, пока сломанный клиент не получит доступ к изменяющимся данным.