Гайд по использованию Lua-скриптов в Redis
Краткий гайд по написанию и запуску Lua-скриптов на Redis.
14К открытий15К показов
Что такое язык Lua?
Язык программирования Lua появился в 1993 году. По своей структуре он является компактным языком программирования, который можно встраивать практически в любое приложение — от World of Warcraft до веб-сервера Nginx. Ну и конечно же, в Redis, о котором далее и пойдёт речь.
Благодаря Lua в Redis возможно встраивать собственные скриптовые расширения для базы данных. Вызов скриптов выглядит следующим образом:
Далее сам Lua-скрипт:
Что более важно, вы можете запускать скрипты в качестве “умных транзакций”. Это позволяет обрабатывать ошибки в рантайме, не останавливая приложение. Безусловно, то, насколько “умными” будут эти транзакции, зависит целиком и полностью от вас.
Ключи и аргументы
Для обращения к скрипту неплохо бы было передать ему ключи и аргументы. Например, как в нижеприведённом коде:
В конце EVAL
мы видим ноль — это количество ключей, переданных скрипту. Но если вместо ноля написать 2 foo bar fizz buzzthen
, первые два элемента foo
и bar
будут переданы в качестве ключей, а fizz
и buzz
— в качестве аргументов.
Ключи доступны Lua-скрипту в таблице KEYS
. Таблица в Lua — это ассоциативный массив, который также используется в качестве массива из одного элемента. Если помимо ключей используются аргументы, они будут доступны в таблице ARGV
, например:
В Lua ..
используется в качестве оператора объединения, так что здесь в качестве возвращаемого значения мы получаем аргумент, привязанный к пробелу, и название ключа, которое было передано в качестве аргумента:
Никакой магии в KEYS, это просто строка, так что нам по-прежнему нужно получить их значение.
Что касается вызова Redis из Lua, можно использовать функцию redis.call()
. Например:
Прим. автора Если в процессе объединения вы получаете ошибку “attempt to concatenate a boolean value” (попытка объединения значений булевого типа), скорее всего name:first
не было присвоено какое-либо значение.
Таким образом, скрипт взял параметр, нашёл значение ключа и вернул в качестве вывода созданную строку.
Однако помимо EVAL
есть и другой способ передать скрипт на сервер. Например, используя аргументы команды redis-cli
. Попробуем написать чуть более сложный скрипт:
Сохраним его как longhello.lua
и выполним в командной строке:
Итак, мы запускаем redis-cli
, добавляем параметры -h
, -p
и -a
для связи с базой данных. Затем идёт --eval
, позволяющий указать имя файла, в виде которого скрипт отправится на сервер. Также нашему скрипту понадобятся ключи и аргументы. В данном случае ключами являются указанные до первой запятой параметры команды.
Сложные скрипты
Следующий случай наглядно демонстрирует, как Lua решает одну небольшую проблему. Допустим, разные пользователи или разработчики увеличивают счётчики в большой структуре данных. Например, region:one
увеличивает count:emea
, count:usa
, count:atlantic
, в то время как region:two
затрагивает лишь count:usa
. Эти счётчики могут быть добавлены позже, но вдруг вам важно убедиться, что добавятся они все разом? Самое время вспомнить про “умные транзакции”.
Добавим все наши регионы в список:
Создадим локальную переменную:
Начнём с переменной-счётчика — он будет считать все операции по увеличению наших округов.
Теперь запросим у Redis все значения списка, относящиеся к первому ключу.
Здесь мы запустили цикл, в котором функция ipairs()
просмотрит каждую задействованную Lua-таблицу и передаст из неё ключ.
С каждым вызовом мы увеличиваем указанный ключ, а заодно и наш счётчик.
После окончания цикла мы получаем конечное число итераций. Сохраним итоговый скрипт и запустим его на сервере:
В таблице будут содержаться следующие значения:
Значения для region.two
:
Но что, если бы во время выполнения скрипта произошла ошибка? Он просто продолжил бы выполняться, игнорируя её. Для вывода деталей ошибки следует использовать redis.pcall()
.
Кэширование скриптов
Для того чтобы не загружать скрипт каждый раз перед выполнением, можно использовать команду SCRIPT LOAD
для загрузки скрипта в кэш. Рассмотрим пример использования из командной строки:
Здесь $(cat broadcast.lua)
превращает наш скрипт в аргумент. А шестнадцатеричное число ниже — SHA1-подпись нашего скрипта. Её можно использовать для его последующего вызова командой EVALSHA
:
Также есть команды для проверки наличия скрипта на сервере и его удаления – SCRIPT EXISTS
и SCRIPT FLUSH
соответственно.
Не всё так просто
После довольно небольшого промежутка времени (по умолчанию — 5 секунд) Lua-скрипты начнут выдавать ошибки в ответ на запросы — в таком случае возможно только “убить” скрипт командой KILL SCRIPT
либо выключить сервер командой SHUTDOWN NOSAVE
. С другой стороны, 5 секунд — очень щедрое ограничение, ведь ваши скрипты должны выполняться буквально в течение миллисекунд. И на это есть очень веская причина: во время выполнения ваших скриптов все остальные процессы приостанавливаются.
Заключение
Итак, в этой статье мы рассмотрели примеры написания простых и сложных Lua-скриптов, их вызов из Redis, а также запись и хранение на сервере.
14К открытий15К показов