Как «EXPIRE» дочернего ключа «HSET» в Redis?

108

Мне нужно истечь срок действия всех ключей в хэше Redis, которые старше 1 месяца.

айевен
источник

Ответы:

117

Это невозможно из соображений простоты Redis .

Куот Антирез, создатель Redis:

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

Supr
источник
8
вот почему Redis - такая замечательная программа. они знают, где это сделать проще
tObi
20

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

При наличии хеша структура в основном выглядит так:

hash_top_key
  - child_key_1 -> some_value
  - child_key_2 -> some_value
  ...
  - child_key_n -> some_value

Поскольку мы хотим добавить TTLдочерние ключи, мы можем переместить их в верхние ключи. Главное, что ключ теперь должен быть комбинацией hash_top_keyдочернего ключа:

{hash_top_key}child_key_1 -> some_value
{hash_top_key}child_key_2 -> some_value
...
{hash_top_key}child_key_n -> some_value

Мы {}намеренно используем обозначения. Это позволяет всем этим ключам попасть в одно и то же hash slot. Вы можете прочитать об этом здесь: https://redis.io/topics/cluster-tutorial

Теперь, если мы хотим проделать ту же операцию с хешами, мы могли бы сделать:

HDEL hash_top_key child_key_1 => DEL {hash_top_key}child_key_1

HGET hash_top_key child_key_1 => GET {hash_top_key}child_key_1

HSET hash_top_key child_key_1 some_value => SET {hash_top_key}child_key_1 some_value [some_TTL]

HGETALL hash_top_key => 
  keyslot = CLUSTER KEYSLOT {hash_top_key}
  keys = CLUSTER GETKEYSINSLOT keyslot n
  MGET keys

Вот интересный HGETALL. Сначала мы получаем hash slotдля всех наших дочерних ключей. Затем мы получаем ключи для этого конкретного hash slotи, наконец, получаем значения. Здесь нам нужно быть осторожными, поскольку для этого может быть больше, чем nключей, hash slotа также могут быть ключи, которые нам не интересны, но у них такие же hash slot. Мы могли бы написать Luaсценарий для выполнения этих действий на сервере, выполнив команду EVALили EVALSHA. Опять же, вам необходимо принять во внимание производительность этого подхода для вашего конкретного сценария.

Еще несколько ссылок:

Хвейга
источник
Этот подход использует больше памяти, чем простые ключи со сроком действия.
VasileM
3

Существует Java-фреймворк Redisson, который реализует хэш- Mapобъект с поддержкой TTL входа. Он использует hmapи zsetобъекты Redis под капотом. Пример использования:

RMapCache<Integer, String> map = redisson.getMapCache('map');
map.put(1, 30, TimeUnit.DAYS); // this entry expires in 30 days

Этот подход весьма полезен.

Никита Кокшаров
источник
а как создать карту? потому что я не нашел ни учебника, ни метода создания / установки
FaNaT
@ ZoltánNémeth В Redis карта создается автоматически при вставке первого значения.
Никита Кокшаров
3

Это возможно в KeyDB, который является форком Redis. Поскольку это вилка, она полностью совместима с Redis и работает как капля замены.

Просто используйте команду EXPIREMEMBER. Он работает с наборами, хешами и отсортированными наборами.

EXPIREMEMBER keyname подключ [время]

Вы также можете использовать TTL и PTTL, чтобы увидеть срок действия

Подраздел имени ключа TTL

Дополнительная документация доступна здесь: https://docs.keydb.dev/docs/commands/#expiremember

Джон Салли
источник
2

Что касается реализации NodeJS, я добавил настраиваемое expiryTimeполе в объект, который сохраняю в HASH. Затем по прошествии определенного периода времени я очищаю просроченные записи HASH, используя следующий код:

client.hgetall(HASH_NAME, function(err, reply) {
    if (reply) {
        Object.keys(reply).forEach(key => {
            if (reply[key] && JSON.parse(reply[key]).expiryTime < (new Date).getTime()) {
                client.hdel(HASH_NAME, key);
            }
        })
    }
});
Георгий И. Цопуридис
источник
Вы можете сделать это более эффективным, используя Array.filterдля создания массива keysдля удаления из хэша, а затем передать его client.hdel(HASH_NAME, ...keys)за один вызов.
doublesharp 01
const keys = Object.keys(reply).filter(key => reply[key] && JSON.parse(reply[key]).expiryTime < Date.now()); client.hdel(HASH_NAME, ...keys);
doublesharp
1

Ты можешь. Вот пример.

redis 127.0.0.1:6379> hset key f1 1
(integer) 1
redis 127.0.0.1:6379> hset key f2 2
(integer) 1
redis 127.0.0.1:6379> hvals key
1) "1"
2) "1"
3) "2"
redis 127.0.0.1:6379> expire key 10
(integer) 1
redis 127.0.0.1:6379> hvals key
1) "1"
2) "1"
3) "2"
redis 127.0.0.1:6379> hvals key
1) "1"
2) "1"
3) "2"
redis 127.0.0.1:6379> hvals key

Используйте команду EXPIRE или EXPIREAT .

Если вы хотите, чтобы срок действия определенных ключей в хэше истек более 1 месяца. Это невозможно. Команда Redis expire предназначена для всех ключей в хэше. Если вы установите ежедневный хэш-ключ, вы можете установить время жизни ключей.

hset key-20140325 f1 1
expire key-20140325 100
hset key-20140325 f1 2
Джонатан Ким
источник
24
Я не думаю, что он хочет истекать срок действия всех ключей в хэше, скорее, всех ключей в хеше, которые старше 1 месяца , поэтому только некоторые из них. Что AFAIK невозможно.
UpTheCreek
1
ИМХО вопрос, допустим это. Итак, Джонатан получил от меня +1, потому что он помог мне! Спасибо!
longliveenduro
как вы истекаете "f1" и "f2"?
vgoklani
4
Это не отвечает на вопрос или совсем не помогает. Необходимо истечь элементы в хэше, а не сам хеш.
Рон
@UpTheCreek Спасибо!
Джонатан Ким
1

Для этого вы можете хранить ключи / значения в Redis по-другому, просто добавляя префикс или пространство имен к вашим ключам при их сохранении, например, "hset_"

  • Получите ключ / значение, GET hset_keyравноеHGET hset key

  • Добавьте ключ / значение, SET hset_key valueравноеHSET hset key

  • Получить все ключи KEYS hset_*равноHGETALL hset

  • Получить все валы нужно за 2 операции, сначала получить все ключи, KEYS hset_*затем получить значение для каждого ключа

  • Добавьте ключ / значение с TTL или истечение срока действия, о котором идет речь:

 SET hset_key value
 EXPIRE hset_key

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

Примечание:

  • KEYSбудет искать соответствующий ключ во всей базе данных, что может повлиять на производительность, особенно если у вас большая база данных. while SCAN 0 MATCH hset_*может быть лучше, если он не блокирует сервер, но все же производительность является проблемой в случае большой базы данных.

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

Спасибо @DanFarrell, который подчеркнул проблему производительности, связанную с KEYS

Мухаммад Солиман
источник
1
просто
Дэниел Фаррелл
как! если вы говорите о временных сложностях, все эти операции имеют ту же временную сложность, что и hashset... получить O (1) установить O (1) получить все O (n)
Мухаммад Солиман
«рассматривать KEYS как команду, которую следует использовать в производственной среде с особой осторожностью». redis.io/commands/KEYS . HGETALL предназначен O(n)для количества вещей в наборе, KEYSдля количества вещей в БД.
Дэниел Фаррелл,
это правда, scan 0 match namespace:*может быть лучше, если он не блокирует сервер
Мухаммад Солиман
1
Кроме того, разделите эти ключи, если они малы, по разным базам данных. спасибо @DanFarrell
Мухаммад Солиман
1

У нас была та же проблема, о которой здесь говорилось.

У нас есть хэш Redis, ключ к хеш-записям (парам имя / значение), и нам нужно было хранить индивидуальное время истечения срока для каждой хеш-записи.

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

Затем, при чтении, мы декодируем префикс и проверяем срок его действия. Это дополнительные накладные расходы, однако чтение по-прежнему O (n), и весь ключ истечет, когда истечет последняя запись хэша.

MikeZ
источник
0

Вы можете использовать уведомления Redis Keyspace, используя psubscribeи "__keyevent@<DB-INDEX>__:expired".

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

Что касается вашего вопроса, в основном вы создаете временный «нормальный» ключ, используя setвремя истечения срока в с / мс. Он должен соответствовать имени ключа, который вы хотите удалить в своем наборе.

Поскольку ваш временный ключ будет опубликован в вашем соединении Redis с сохранением, "__keyevent@0__:expired"когда истечет срок его действия, вы можете легко удалить свой ключ из исходного набора, так как в сообщении будет указано имя ключа.

Простой пример на практике на этой странице: https://medium.com/@micah1powell/using-redis-keyspace-notifications-for-a-reminder-service-with-node-c05047befec3

документ: https://redis.io/topics/notifications (ищите флаг xE)

солярийдым
источник
0

Вы можете использовать Sorted Set в redis, чтобы получить контейнер TTL с отметкой времени в качестве оценки. Например, всякий раз, когда вы вставляете строку события в набор, вы можете установить его счет на время события. Таким образом, вы можете получить данные любого временного окна, позвонив zrangebyscore "your set name" min-time max-time

Более того, мы можем истечь, используя zremrangebyscore "your set name" min-time max-timeдля удаления старых событий.

Единственным недостатком здесь является то, что вам придется выполнять служебные обязанности стороннего процесса, чтобы поддерживать размер набора.

моджиане
источник
-1

Вы можете легко истечь хэши Redis, например, используя python

import redis
conn = redis.Redis('localhost')
conn.hmset("hashed_user", {'name': 'robert', 'age': 32})
conn.expire("hashed_user", 10)

Это истекут все дочерние ключи в хэш hashed_user через 10 секунд

то же самое из redis-cli,

127.0.0.1:6379> HMSET testt username wlc password P1pp0 age 34
OK
127.0.0.1:6379> hgetall testt
1) "username"
2) "wlc"
3) "password"
4) "P1pp0"
5) "age"
6) "34"
127.0.0.1:6379> expire testt 10
(integer) 1
127.0.0.1:6379> hgetall testt
1) "username"
2) "wlc"
3) "password"
4) "P1pp0"
5) "age"
6) "34"

через 10 секунд

127.0.0.1:6379> hgetall testt
(empty list or set)
Томас Джон
источник
7
вопрос про истекший hsetребенок не полный hset.
thangdc94
не отвечает на заданный вопрос
peteclark3