Как на самом деле работают инструкции redis watch() и multi()?

Я пытаюсь понять правильное использование инструкции multi() и watch() для доступа к базе данных Redis по redis-py версии 3.5.3. Версия сервера Redis — сервер Redis v=5.0.5.

В частности, я написал и выполнил следующий код, где используется инструкция watch с ключом keyWatch:

r = redis.Redis()

def key_incr():
    print('keyWatch before incr = ' + r.get('keyWatch').decode("utf-8"))
    pipe = r.pipeline()
    pipe.watch('keyWatch')
    pipe.multi()
    pipe.incr('keyWatch')
    pipe.execute()
    print('keyWatch after incr = ' + r.get('keyWatch').decode("utf-8"))

key_incr()

Предыдущий код может быть выполнен правильно, и его вывод (начальное значение keyWatch равно 9):

keyWatch before incr = 9
keyWatch after incr = 10

Если я уберу инструкцию multi() из кода, она станет:

r = redis.Redis()

def key_incr():
    print('keyWatch before incr = ' + r.get('keyWatch').decode("utf-8"))
    pipe = r.pipeline()
    pipe.watch('keyWatch')
    # NOTE: here the multi() instruction is commented
    #pipe.multi()
    pipe.incr('keyWatch')
    pipe.execute()
    print('keyWatch after incr = ' + r.get('keyWatch').decode("utf-8"))

key_incr()

Его выполнение вызывает следующее исключение:

raise WatchError("Watched variable changed.")
redis.exceptions.WatchError: Watched variable changed.

Мне нужно, чтобы избежать этого, другие клиенты изменяют ключ keyWatch внутри транзакции, но почему в моем примере кода исключение WatchError возникает только в том случае, если инструкция multi() отсутствует?

Спасибо


РЕДАКТИРОВАТЬ
По redis-cli monitor (МОНИТОР в остальной части поста) я могу видеть запросы к серверу во время выполнения предыдущих 2 фрагментов кода.
В случае с инструкцией multi() у меня следующие пожелания:

> redis-cli monitor
OK
1681733993.273545 [0 127.0.0.1:46342] "GET" "keyWatch"
1681733993.273790 [0 127.0.0.1:46342] "WATCH" "keyWatch"
1681733993.273934 [0 127.0.0.1:46342] "MULTI"
1681733993.273945 [0 127.0.0.1:46342] "INCRBY" "keyWatch" "1"
1681733993.273950 [0 127.0.0.1:46342] "EXEC"
1681733993.274279 [0 127.0.0.1:46342] "GET" "keyWatch"

На случай без инструкции multi() у меня следующие пожелания:

> redis-cli monitor
OK
1681737498.462228 [0 127.0.0.1:46368] "GET" "keyWatch"
1681737498.462500 [0 127.0.0.1:46368] "WATCH" "keyWatch"
1681737498.462663 [0 127.0.0.1:46368] "INCRBY" "keyWatch" "1"
1681737498.463072 [0 127.0.0.1:46368] "MULTI"
1681737498.463081 [0 127.0.0.1:46368] "EXEC"

Также во втором случае присутствует инструкция MULTI, но между ней и EXEC нет запросов.
Исключение keyWatch вызывается инструкцией EXEC, поскольку МОНИТОР не показывает последний запрос "GET" "keyWatch" (сравните с первым журналом МОНИТОРА, чтобы найти последний запрос "GET" "keyWatch").

Все это наводит меня на мысль, что исключение вызвано выполнением:
"INCRBY" "keyWatch" "1" вне блока MULTI/EXEC.

Если кто-то может подтвердить это и объяснить лучше, поведение приветствуется.

Спасибо

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
130
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Часы WATCH , MULTI и EXEC предназначены для совместной работы. В частности, вызовы MULTI и EXEC позволяют изолированно выполнять поставленные в очередь команды. Redis называет это транзакцией.

Вот как это работает:

MULTI                          <- start queueing commands
INCR someKey                   <- queue this command
SET someOtherKey someValue     <- queue this command
UNLINK someThirdKey            <- queue this command
EXEC                           <- execute all the queued commands

Пока эти команды находятся в очереди, могут поступать другие команды. Эти другие команды могут изменить ключи, являющиеся частью транзакции, что может быть недопустимым. Введите СМОТРЕТЬ.

WATCH привык, ну, следить за этими ключами. Если они были изменены к моменту вызова EXEC, EXEC вернет ошибку. Затем вам нужно запустить код, чтобы повторить транзакцию (или, возможно, сгенерировать ошибку, в зависимости от ваших потребностей).

Если они не были изменены, то EXEC выполняет все команды из очереди, и жизнь продолжается.

Это работает следующим образом:

WATCH someKey someOtherKey     <- watch these for changes
MULTI                          <- start queueing commands
INCR someKey                   <- queue this command
SET someOtherKey someValue     <- queue this command
UNLINK someThirdKey            <- queue this command
EXEC                           <- either error or execute queued commands

Обратите внимание, что вам не нужно отслеживать ключ как часть транзакции, как показано в моем примере выше. В этом случае мне все равно, если кто-то изменит то, что я собираюсь удалить.

Транзакции в Redis — это не то, что часто думают разработчики. Если вы хотите углубиться в детали, на сайте Redis есть руководство по транзакциям.

Спасибо. Осталось понять, почему в моем коде возникает исключение, если я удаляю multi() инструкцию: в этом случае (с redis-cli monitor) я вижу, что инструкции MULTI/EXEC выполняются несмотря на то, что инструкции multi() нет, а между MULTI/EXEC есть невыполненные инструкции. Ваш ответ подсказывает мне, что EXEC видит модификацию keyWatch и вызывает исключение. Вместо этого, если присутствует multi(), модификация keyWatch происходит между MULTI/EXEC, и это предотвращает Исключение. Это правильно?

frankfalse 12.04.2023 16:56

Я предполагаю, что redis-py ставится в очередь от вашего имени, когда вы вызываете .multi(), а затем, когда вы вызываете .exec(), он отправляет то, что было поставлено в очередь на клиенте. Если .multi() никогда не вызывается, redis-py имеет пустую очередь и поэтому отправляет ее.

Guy Royse 12.04.2023 19:35

Суть в том, что вызов EXEC без MULTI не имеет семантического смысла. EXEC предполагает, что был вызван MULTI. Вот как это работает.

Guy Royse 12.04.2023 19:36

Другие вопросы по теме