Подтвердить что ты не робот

Как создать распределенный замок с 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?

4b9b3361

Ответ 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

Ответ 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 исправить все описанные проблемы. Он изолирует сломанных клиентов, и никто не может получить блокировку до тех пор, пока сломанный клиент не получит доступ к изменяющимся данным.