Como “EXPIRAR” a chave filha “HSET” no redis?

107

Preciso expirar todas as chaves no hash redis, que têm mais de 1 mês.

Aieven
fonte

Respostas:

117

Isso não é possível , para manter o Redis simples .

Quoth Antirez, criador do Redis:

Olá, não é possível, usar uma chave de nível superior diferente para aquele campo específico ou armazenar junto com o campo outro campo com um tempo de expiração, buscar ambos e deixar que o aplicativo entenda se ainda é válido ou não com base em hora atual.

Supr
fonte
8
é por isso que o redis é um software tão incrível. eles sabem onde manter as coisas simples
até
20

O Redis não oferece suporte TTLpara hashes diferentes da chave superior, que expiraria todo o hash. Se você estiver usando um cluster fragmentado, há outra abordagem que você pode usar. Essa abordagem pode não ser útil em todos os cenários e as características de desempenho podem ser diferentes das esperadas. Ainda vale a pena mencionar:

Ao ter um hash, a estrutura basicamente se parece com:

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

Visto que queremos adicionar TTLas chaves filhas, podemos movê-las para as chaves superiores. O ponto principal é que a chave agora deve ser uma combinação de uma hash_top_keychave filha:

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

Estamos usando a {}notação de propósito. Isso permite que todas as chaves caiam na mesma hash slot. Você pode ler mais sobre isso aqui: https://redis.io/topics/cluster-tutorial

Agora, se quisermos fazer a mesma operação de hashes, poderíamos fazer:

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

O interessante aqui é HGETALL. Primeiro, obtemos as hash slotchaves para todos os nossos filhos. Em seguida, obtemos as chaves para aquele particular hash slote, finalmente, recuperamos os valores. Precisamos ter cuidado aqui, pois pode haver mais do que nchaves para isso hash slote também pode haver chaves nas quais não estamos interessados, mas que têm as mesmas hash slot. Na verdade, poderíamos escrever um Luascript para fazer essas etapas no servidor executando um comando EVALou EVALSHA. Novamente, você precisa levar em consideração o desempenho dessa abordagem para seu cenário específico.

Mais algumas referências:

hveiga
fonte
Essa abordagem usa mais memória do que chaves simples com tempo de expiração.
VasileM
3

Existe um framework Java Redisson que implementa Mapobjeto hash com suporte TTL de entrada. Ele usa hmape zsetobjetos Redis sob o capô. Exemplo de uso:

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

Essa abordagem é bastante útil.

Nikita Koksharov
fonte
mas como você pode criar um mapa? porque não encontrei nenhum tutorial ou método de criar / definir
FaNaT
@ ZoltánNémeth No mapa Redis criado automaticamente quando o primeiro valor é inserido.
Nikita Koksharov
3

Isso é possível no KeyDB, que é um fork do Redis. Por ser um Fork, é totalmente compatível com o Redis e funciona como um substituto.

Basta usar o comando EXPIREMEMBER. Funciona com conjuntos, hashes e conjuntos classificados.

EXPIREMEMBER subchave de nome-chave [hora]

Você também pode usar TTL e PTTL para ver a validade

Subchave TTL keyname

Mais documentação está disponível aqui: https://docs.keydb.dev/docs/commands/#expiremember

John Sully
fonte
2

Em relação a uma implementação de NodeJS, adicionei um expiryTimecampo personalizado no objeto que salvo no HASH. Então, após um período específico, eu limpo as entradas HASH expiradas usando o seguinte código:

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);
            }
        })
    }
});
George I. Tsopouridis
fonte
Você pode tornar isso mais eficiente usando o Array.filterpara criar uma matriz de keyspara excluir do hash e, em seguida, passar para client.hdel(HASH_NAME, ...keys)em uma única chamada.
doublesharp
const keys = Object.keys(reply).filter(key => reply[key] && JSON.parse(reply[key]).expiryTime < Date.now()); client.hdel(HASH_NAME, ...keys);
doublesharp
1

Você pode. Aqui está um exemplo.

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

Use EXPIRE ou EXPIREAT comando.

Se você deseja expirar chaves específicas no hash com mais de 1 mês. Isso não é possível. O comando de expiração do Redis é para todas as chaves no hash. Se você definir a chave hash diária, poderá definir uma hora de vida das chaves.

hset key-20140325 f1 1
expire key-20140325 100
hset key-20140325 f1 2
Jonathan Kim
fonte
24
Não acho que ele queira expirar todas as chaves do hash, mas sim todas as chaves do hash com mais de 1 mês , portanto, apenas algumas delas. Qual AFAIK não é possível.
UpTheCreek
1
IMHO a questão, vamos assumir isso. Então Jonathan ganha +1 de mim, porque ele me ajudou! Obrigado!
longliveenduro
como você expira "f1" e "f2"?
vgoklani
4
Isso não responde à pergunta ou ajuda em nada. A necessidade é expirar os elementos do hash, não o próprio hash.
Ron
@UpTheCreek Obrigado!
Jonathan Kim
1

Você poderia armazenar chaves / valores no Redis de maneira diferente para conseguir isso, apenas adicionando um prefixo ou namespace às suas chaves ao armazená-las, por exemplo, "hset_"

  • Obtenha uma chave / valor GET hset_keyigual aHGET hset key

  • Adicionar uma chave / valor SET hset_key value igual aHSET hset key

  • Pegue todas as chaves KEYS hset_* iguais aHGETALL hset

  • Obter todos os vals deve ser feito em 2 operações, primeiro pegue todas as chaves e, em KEYS hset_*seguida, obtenha o valor de cada chave

  • Adicione uma chave / valor com TTL ou expire, o que é o tópico da questão:

 SET hset_key value
 EXPIRE hset_key

Nota : KEYSirá pesquisar a correspondência da chave em todo o banco de dados, o que pode afetar o desempenho, especialmente se você tiver um banco de dados grande.

Nota:

  • KEYSirá pesquisar a correspondência da chave em todo o banco de dados, o que pode afetar o desempenho, especialmente se você tiver um banco de dados grande. enquantoSCAN 0 MATCH hset_* pode ser melhor, desde que não bloqueie o servidor, mas ainda assim o desempenho é um problema no caso de banco de dados grande.

  • Você pode criar um novo banco de dados para armazenar separadamente essas chaves que deseja expirar, especialmente se forem um pequeno conjunto de chaves.

Agradecimentos a @DanFarrell que destacou o problema de desempenho relacionado a KEYS

Muhammad Soliman
fonte
1
apenas esteja ciente de que essa é uma mudança significativa nas características de desempenho
Daniel Farrell
como! se você está falando sobre complexidades de tempo, todas essas operações têm a mesma complexidade de tempo que hashset.. get O (1) set O (1) get all O (n)
Muhammad Soliman
"considere KEYS como um comando que só deve ser usado em ambientes de produção com extremo cuidado." redis.io/commands/KEYS . HGETALL é O(n)para número de coisas no conjunto, KEYSpara número de coisas no banco de dados.
Daniel Farrell
isso é verdade, scan 0 match namespace:*pode ser melhor, desde que não bloqueie o servidor
Muhammad Soliman
1
Além disso, separe essas chaves, se forem pequenas, em um banco de dados diferente. obrigado @DanFarrell
Muhammad Soliman
1

Tivemos o mesmo problema discutido aqui.

Temos um hash Redis, uma chave para entradas de hash (pares nome / valor) e precisamos manter os tempos de expiração individuais em cada entrada de hash.

Implementamos isso adicionando n bytes de dados de prefixo contendo informações de expiração codificadas quando gravamos os valores de entrada de hash, também definimos a chave para expirar no momento contido no valor sendo escrito.

Então, na leitura, decodificamos o prefixo e verificamos a validade. Esta é uma sobrecarga adicional, entretanto, as leituras ainda são O (n) e a chave inteira expirará quando a última entrada hash expirar.

MikeZ
fonte
0

Você pode usar as notificações do Redis Keyspace usando psubscribee "__keyevent@<DB-INDEX>__:expired".

Com isso, cada vez que uma chave expirar, você receberá uma mensagem publicada em sua conexão do redis.

Em relação à sua pergunta basicamente você cria uma chave "normal" temporária setcom um tempo de expiração em s / ms. Deve corresponder ao nome da chave que você deseja excluir do seu conjunto.

Como sua chave temporária será publicada na conexão do redis com o "__keyevent@0__:expired"quando expirar, você pode excluir facilmente sua chave do conjunto original, pois a mensagem terá o nome da chave.

Um exemplo simples na prática nessa página: https://medium.com/@micah1powell/using-redis-keyspace-notifications-for-a-reminder-service-with-node-c05047befec3

doc: https://redis.io/topics/notifications (procure o sinalizador xE)

solarissmoke
fonte
0

Você pode usar Sorted Set no redis para obter um contêiner TTL com carimbo de data / hora como pontuação. Por exemplo, sempre que você inserir uma string de evento no conjunto, poderá definir sua pontuação para a hora do evento. Assim, você pode obter dados de qualquer janela de tempo chamando zrangebyscore "your set name" min-time max-time

Além disso, podemos expirar usando zremrangebyscore "your set name" min-time max-timepara remover eventos antigos.

A única desvantagem aqui é que você precisa fazer a manutenção de um processo externo para manter o tamanho do conjunto.

mojians
fonte
-1

Você pode expirar hashes Redis facilmente, por exemplo, usando python

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

Isso irá expirar todas as chaves filhas em hash hashed_user após 10 segundos

o mesmo de 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"

após 10 segundos

127.0.0.1:6379> hgetall testt
(empty list or set)
Thomas John
fonte
6
pergunta é sobre expirar hsetfilho não o completo hset.
thangdc94
não responde à pergunta feita
peteclark3