Redis: Почему сценарии Lua заменяют транзакции?

В документации для транзакций говорится:

"мы можем отказаться и окончательно удалить транзакции" и "все, что вы может сделать с транзакцией Redis, вы также можете сделать с script"

http://redis.io/topics/transactions

Но не так ли? Я вижу проблему с этим.

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

EVAL script не позволит вам это сделать. Согласно документации на этой странице:

"Скрипты как чистые функции... script всегда оценивает то же самое Redis записывает команды с теми же аргументами, что и те же входные данные задавать. Операции, выполняемые script, не могут зависеть от каких-либо скрытых (неявная) информация или состояние, которое может меняться как scriptисполнения или между различными исполнениями script, а также может ли он зависеть от любого внешнего входа от устройств ввода/вывода".

http://redis.io/commands/eval

Проблема, которую я вижу с EVAL, заключается в том, что вы не можете получить состояние этих переменных внутри script и создать уникальный набор записей, основанных на состоянии этих переменных. Опять же: "script всегда оценивает те же команды записи Redis с теми же аргументами, что и тот же набор входных данных". Таким образом, полученные записи уже определены (кэшируются с первого запуска), а EVAL script не волнует, какие значения GET находятся внутри script. Единственное, что вы можете сделать, это выполнить GET для этих переменных перед вызовом EVAL, а затем передать эти переменные в EVAL script, но вот проблема: теперь у вас есть проблема атомарности между вызовом GET и вызовом EVAL.

Другими словами, все переменные, которые вы бы сделали WATCH для транзакции, в случае EVAL вам вместо этого нужно ПОЛУЧИТЬ эти переменные, а затем передать их в EVAL script. Поскольку атомный характер script не гарантируется до тех пор, пока фактически не запустится script, и вам нужно ПОЛУЧИТЬ эти переменные перед вызовом EVAL для запуска script, который оставляет открытие, где состояние этих переменных может измениться между GET и передачей их EVAL. Поэтому гарантия атомарности, которую у вас есть с WATCH, у вас нет с EVAL для очень важного набора вариантов использования.

Так почему же говорят об отказе от транзакций, когда это может привести к потере важной функциональности Redis? Или есть ли способ сделать это с помощью сценариев EVAL, которые я пока не понимаю? Или запланированы ли функции, которые могут решить это для EVAL? (Гипотетический пример: если они сделали WATCH работать с EVAL так же, как WATCH работает с EXEC, это может сработать.)

Есть ли решение? Или я должен понять, что Redis не может быть полностью безопасным в долгосрочной перспективе?

Ответ 1

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

EVAL script не позволяет просматривать переменные

Когда выполняется eval script, ничто другое не может запускаться одновременно. Итак, переменные watch ing бессмысленны. Вы можете быть уверены, что никто не изменил переменные после того, как вы прочитали значения в script.

Проблема, которую я вижу с EVAL, заключается в том, что вы не можете получить состояние этих переменных внутри script и создать уникальный набор записей, основанных на состоянии этих переменных.

Не верно. Вы можете передать ключи на eval script. В пределах вашего eval script вы можете прочитать значения из Redis, а затем на основе этих значений условно выполнить другие команды.

script по-прежнему детерминирован. Если вы возьмете этот script и запустите его на подчиненном устройстве, он все равно выполнит те же команды записи, что и ведущий и ведомый будут иметь одинаковые данные.

Ответ 2

Скрипты EVAL фактически расширяют и упрощают функциональность транзакций.

Это может помочь просмотреть два типа транзакций в Redis следующим образом:

1. процедурный (MULTI EXEC)

Pure MULTI EXEC группирует команды, которые должны выполняться за один раз, и возвращает массив ответов из каждой команды. Это имеет серьезные ограничения. Он не позволяет использовать промежуточный результат из одной команды в следующей внутри транзакции. В этом чистом виде это не очень полезно в практических приложениях. Это в основном конвейерная обработка с гарантированной атомарностью.

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

2. (сценарии EVAL)

EVAL не только группирует команды Redis, но также дает вам полный язык программирования, в частности условия, циклы и локальные переменные. В Lua script вы можете читать значения из Redis в одной команде и использовать их позже в следующей команде.

Вы отправляете script, который выполняется атомарным способом. Это гарантируется однопоточным, "останавливающим мир". Никакая другая команда script или Redis не будет выполнена во время выполнения script. Следовательно, EVAL также имеет ограничение: скрипты должны быть небольшими и быстрыми, чтобы предотвратить блокирование других клиентов.

Нам также нужно верить, что другие клиенты не отправляют медленный script, который следует рассматривать как ошибку программирования. Он хорошо вписывается в модель безопасности, поскольку "Redis предназначен для доступа к доверенным клиентам внутри доверенных сред".

Ответ 3

Сценарий EVAL может сделать все, что может сделать транзакция, за исключением оптимистической блокировки, которая не очень полезна в однопоточной среде.

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