Вопрос по – Транзакции и смотреть заявление в Redis
Не могли бы вы объяснить мне следующий пример из "Маленькой книги Redis":
С помощью приведенного выше кода мы не сможем реализовать нашу собственную команду incr, поскольку все они выполняются вместе после вызова exec. Из кода мы не можем сделать:
redis.multi()
current = redis.get('powerlevel')
redis.set('powerlevel', current + 1)
redis.exec()
Это не так, как транзакции Redis работают. Но, если мы добавим часы на уровень мощности, мы можем сделать:
redis.watch('powerlevel')
current = redis.get('powerlevel')
redis.multi()
redis.set('powerlevel', current + 1)
redis.exec()
Если другой клиент изменит значение powerlevel после того, как мы вызовем его, наша транзакция не будет выполнена. Если ни один клиент не изменит значение, набор будет работать. Мы можем выполнить этот код в цикле, пока он не заработает.
Почему мы не можем выполнить инкремент транзакции, который не может быть прерван другой командой? Почему нам нужно итерировать вместо этого и ждать, пока никто не изменит значениед транзакция начинается?
1) Почему мы не можем выполнить инкремент транзакции, который не может быть прерван другой командой?
Пожалуйста, обратите внимание, что «транзакции» Redis полностью отличаются от того, что большинство людей считают транзакциями в классической СУБД.
# Does not work
redis.multi()
current = redis.get('powerlevel')
redis.set('powerlevel', current + 1)
redis.exec()
Вам нужно понимать, что выполняется на стороне сервера (в Redis), а что выполняется на стороне клиента (в вашем сценарии). В приведенном выше коде команды GET и SET будут выполняться на стороне Redis, но предполагается, что присвоение current и вычисление current + 1 будут выполняться на стороне клиента.
Чтобы гарантировать атомарность, блок MULTI / EXEC задерживает выполнение команд Redis до исполнения. Таким образом, клиент будет только накапливать команды GET и SET в памяти и выполнять их одним выстрелом и атомарно в конце. Конечно, попытка присвоить ток результату GET и приращению произойдет задолго до этого. На самом деле метод redis.get будет возвращать только строку «QUEUED», указывающую на задержку выполнения команды, и увеличение не будет работать.
В блоках MULTI / EXEC вы можете использовать только те команды, параметры которых могут быть полностью известны до начала блока. Вы можете прочитать документация за дополнительной информацией
2) Почему мы должны вместо этого повторять и ждать, пока никто не изменит значение, прежде чем транзакция начнется?
Это пример одновременной оптимистичной картины.
Если бы мы не использовали WATCH / MULTI / EXEC, у нас было бы потенциальное состояние гонки:
# Initial arbitrary value
powerlevel = 10
session A: GET powerlevel -> 10
session B: GET powerlevel -> 10
session A: current = 10 + 1
session B: current = 10 + 1
session A: SET powerlevel 11
session B: SET powerlevel 11
# In the end we have 11 instead of 12 -> wrong
Теперь давайте добавим блок WATCH / MULTI / EXEC. С предложением WATCH команды между MULTI и EXEC выполняются, только если значение не изменилось.
# Initial arbitrary value
powerlevel = 10
session A: WATCH powerlevel
session B: WATCH powerlevel
session A: GET powerlevel -> 10
session B: GET powerlevel -> 10
session A: current = 10 + 1
session B: current = 10 + 1
session A: MULTI
session B: MULTI
session A: SET powerlevel 11 -> QUEUED
session B: SET powerlevel 11 -> QUEUED
session A: EXEC -> success! powerlevel is now 11
session B: EXEC -> failure, because powerlevel has changed and was watched
# In the end, we have 11, and session B knows it has to attempt the transaction again
# Hopefully, it will work fine this time.
Так что вам не нужно повторять, чтобы подождать, пока никто не изменит значение, а пытаться выполнить операцию снова и снова, пока Redis не удостоверится, что значения согласованы и не сигнализирует об успешност
В большинстве случаев, если «транзакции» выполняются достаточно быстро и вероятность возникновения разногласий невелика, обновления очень эффективны. Теперь, если есть конфликт, некоторые дополнительные операции должны быть выполнены для некоторых «транзакций» (из-за итерации и повторных попыток). Но данные всегда будут согласованы и блокировка не требуется.