Как сохранить в Redis отсортированный набор с отметкой времени на стороне сервера в качестве оценки?
Я хочу использовать отсортированный набор для хранения объектов, используя временную метку redis-server в качестве оценки.
Я знаю, что могу использовать Redis Streams с *
id, но у Redis Streams есть ограничения, в том числе я не могу редактировать объекты, я не могу использовать ранговую или лексикографическую сортировку, я не могу удалять объекты в середине, объединение или пересечение и т. д.
Я хочу сделать это атомарно и использовать временную метку redis-server, чтобы можно было использовать несколько клиентов для ZADD
не беспокоясь о синхронизации часов.
Как это сделать?
1 ответ
Решение - использовать скрипт Lua:
local time = redis.call('TIME')
local ts = time[1]..string.format('%06d', time[2])
return redis.call('ZADD', KEYS[1], ts, ARGV[1])
Здесь мы используем Redis TIME
команда. Команда возвращает:
- время unix в секундах
- микросекунды
Итак, мы можем объединить эти два и использовать временную метку в микросекундах. Нам нужно обнулить часть микросекунд.
Так как отсортированные наборы хороши с целыми числами до 2^53, наша временная метка безопасна вплоть до 2255 года.
Это Redis-Cluster-safe, поскольку мы храним в одном ключе. Чтобы использовать несколько ключей, убедитесь, что они размещены на одном узле с помощью хэш-тегов, если вы хотите сравнить временные метки.
Вы можете изменить сценарий, чтобы использовать разрешение ниже микросекунды.
Здесь EVAL
команда, простой ключ доступа и значение в качестве аргументов, нет необходимости создавать отсортированный набор заранее:
EVAL "local time = redis.call('TIME') local ts = time[1]..string.format('%06d', time[2]) return redis.call('ZADD', KEYS[1], ts, ARGV[1])" 1 ssetKey myVal
Как всегда, вы можете загрузить скрипт и использовать EVALSHA
.
> SCRIPT LOAD "local time = redis.call('TIME') local ts = time[1]..string.format('%06d', time[2]) return redis.call('ZADD', KEYS[1], ts, ARGV[1])"
"81e366e422d0b09c9b395b5dfe03c03c3b7b3bf7"
> EVALSHA 81e366e422d0b09c9b395b5dfe03c03c3b7b3bf7 1 ssetKey myNewVal
(integer) 1
Замечание о версии Redis. Если вы используете:
- Версия Redis до 3.2: извините, вы не можете использовать
TIME
(недетерминированная команда), а затем напишите сZADD
. - Версия Redis выше 3.2, но < 5.0: Добавить
redis.replicate_commands()
поверх сценария. Смотрите скрипты как чистые функции - Redis 5.0 и выше: все хорошо.