Транзакции и Lua
Тема дорожной карты · Redis
Транзакции Redis и Lua-скриптинг — два взаимодополняющих механизма для атомарного выполнения нескольких команд в in-memory хранилище без чередования с другими клиентами. Транзакция Redis открывается командой MULTI, за которой следуют ставящиеся в очередь команды, и атомарно фиксируется командой EXEC; если до EXEC происходит ошибка клиента, транзакцию можно отменить командой DISCARD. Lua-скриптинг через команду EVAL более мощный: весь скрипт выполняется как единый атомарный блок внутри Redis-сервера с доступом ко всем ключам, обеспечивая сложные паттерны «чтение-изменение-запись», которые транзакции сами по себе не могут выразить элегантно. В контексте распределённого кэша Lua-скрипты применяются для операций вроде условного истечения, атомарных проверок порогов счётчиков или паттернов CAS (compare-and-swap), требующих чтения значения и условной записи на его основе. Как транзакции Redis, так и Lua-скрипты гарантируют атомарность, но не изоляцию в смысле ACID — другие клиенты блокируются от выполнения между командами, однако Redis не откатывает ошибки на уровне команд внутри транзакции, как это делают реляционные базы данных.
Как это работает
Транзакции и Lua имеет два инструмента атомарности. MULTI/EXEC ставит команды в очередь; они выполняются как один блок — но нет rollback (Redis не ACID-D, изоляции тоже нет, если читаете между командами). WATCH добавляет optimistic locking — EXEC валится, если watched-ключ изменился. EVAL / EVALSHA исполняет Lua-скрипты атомарно на сервере; скрипт — одна неделимая операция. Lua — правильный инструмент для compound read+write, которые должны быть атомарными.
Когда применять
MULTI/EXEC — для "выполнить эти N команд как единицу без других команд между". WATCH + MULTI/EXEC — для compare-and-swap. Lua (EVAL) — для сложной атомарной логики (rate limiter, debounce, distributed semaphore). Всегда кешируйте SHA (EVALSHA) вместо повторной отправки тела скрипта — экономит bandwidth и парсинг.
Типичные ошибки
Ловушки Транзакции и Lua: расчёт, что MULTI/EXEC — транзакция в SQL-смысле (нет — без rollback); долгие Lua-скрипты блокируют однопоточный сервер (всё ждёт EXEC); не обрабатывают nil от EXEC (WATCH-конфликт — retry); Lua-код, мутирующий ключ на основе текущего значения без WATCH (race condition при разделении на команды).