Redis
 sql >> Base de Dados >  >> NoSQL >> Redis

Introdução às estruturas de dados do Redis:hashes

Os hashes Redis são (intuitivamente!) hashes que mapeiam nomes de strings para valores de strings. Eles são essencialmente contêineres nomeados de campos exclusivos e seus valores. Eles são a maneira perfeita de representar um objeto como uma estrutura de dados Redis. Como esperado, eles fornecem operações básicas em tempo constante, como obter, definir, existir, etc. Várias operações avançadas também são fornecidas. A lista completa de comandos de hash está aqui.


Vamos dar uma olhada no redis-cli .

# hmset key field value [field value ...] :  Insert elements in a hash. O(N), N is # of field being set
127.0.0.1:6379> hmset std:101 name "John Smith" dob "01-01-2000" gender M active 0 cgpa 2.9
OK

# hgetall key : key all keys and values in the hash. O(N), N is size of hash
127.0.0.1:6379> hgetall std:101
 1) "name"
 2) "John Smith"
 3) "dob"
 4) "01-01-2000"
 5) "gender"
 6) "M"
 7) "active"
 8) "0"
 9) "cgpa"
10) "2.9"
127.0.0.1:6379> hmset std:102 name "Jane" name "Ann"
OK
# If duplicates are found, only the last set is valid
127.0.0.1:6379> hgetall std:102
1) "name"
2) "Ann"

# hexists key field: does field exist in the hash with key. O(1)
127.0.0.1:6379> hexists std:102 cgpa
(integer) 0

# hincrby key field increment: Increment the integer field by increment. O(1)
127.0.0.1:6379> hincrby std:101 active 1
(integer) 1

# hget key field : the value for field in the hash stored at key. O(1)
127.0.0.1:6379> hget std:101 active
1) "1"
# If field doesn't exist, hincrby sets it to 0 and then applies increment
127.0.0.1:6379> hincrby std:102 active 2
(integer) 2

# hmget key field [field ...]: the values of the fields requested for the hash with key. O(N), N is # of keys requested
127.0.0.1:6379> hmget std:102 active
1) "2"

# hincrbyfloat key field increment: Increment the float value in field by increment. O(1) 
127.0.0.1:6379> HINCRBYFLOAT std:101 cgpa 1.0
"3.9"

# HSETNX key field value: set field to value if not alread set. O(1)
127.0.0.1:6379> hsetnx std:102 cgpa 4.0
(integer) 1
127.0.0.1:6379> hget std:102 cgpa
"4.0"

# hlen key: number of fields in the hash. O(1)
127.0.0.1:6379> hlen std:101
(integer) 5

# hkeys key : all fields in the hash. O(N), N is size of hash
127.0.0.1:6379> hkeys std:101
1) "name"
2) "dob"
3) "gender"
4) "active"
5) "cgpa"

Como esperamos de nossa hospedagem do Redis™* como um servidor de estrutura de dados, vemos que o Redis oferece operações bastante úteis e avançadas em hashes.

Internos

Assim como os conjuntos Redis, os hashes Redis também são implementados como dicionários. Os dicionários no Redis são implementados como tabelas de hash que usam a função de hash MurmurHash2 e crescem por meio de redimensionamento incremental. As colisões de hash são tratadas por encadeamento. Mais detalhes podem ser encontrados na implementação Redis do dicionário em dict.c.
Assim como em Sets, há otimização de armazenamento feita para hashes menores. Essa estrutura de dados é chamada de ziplist (os hashes eram otimizados usando uma estrutura de dados diferente chamada zipmap antes do Redis 2.6) na implementação do Redis. É essencialmente uma lista duplamente encadeada especialmente codificada que é otimizada para economia de memória. Os dados, bem como os ponteiros são armazenados em linha. O Ziplist também é usado para otimizar o armazenamento de conjuntos e listas classificadas menores. Um hash quando achatado em tal lista se parece com [chave1, valor1, chave2, valor2, ...]. Como isso é mais eficiente do que as teclas simples? Hashes com poucas chaves podem ser empacotados de forma inteligente nessa estrutura linear de matriz (ou seja, a lista zip) enquanto ainda garante desempenho O(1) amortizado para obter e definir. Obviamente, isso não pode acompanhar à medida que os campos de hash aumentam. À medida que o hash cresce, ele é convertido na estrutura de dicionário padrão para manter o desempenho O(1) e a economia de espaço é perdida. Os parâmetros de configuração do Redis que controlam essa transformação são:

  • list-max-ziplist-entries default (512):mude para a representação padrão se o hash for maior que esse limite.
  • lista-max-ziplist-value default (64):mude para a representação padrão se o maior elemento do hash se tornar maior que esse limite.

Mais detalhes podem ser entendidos a partir do código e comentários na implementação encontrados aqui. A economia de memória com o uso dessa otimização especial é significativa. Falaremos sobre isso com mais detalhes na próxima seção.

Otimização de memória

Uma das recomendações bem conhecidas para economia de memória ao usar o Redis é usar hashes em vez de strings simples. Este é um caso de uso importante para utilizar o poder dos hashes Redis em aplicativos do mundo real. Da documentação oficial do Redis sobre otimização de memória:

Use hashes quando possível

Pequenos hashes são codificados em um espaço muito pequeno, então você deve tentar representar seus dados usando hashes sempre que possível. Por exemplo, se você tiver objetos representando usuários em um aplicativo da Web, em vez de usar chaves diferentes para nome, sobrenome, email, senha, use um único hash com todos os campos obrigatórios.

Esse post então propõe uma maneira de mapear um intervalo de objetos em um conjunto de hashes para aproveitar a economia de memória. O Instagram, em uma postagem de blog muito popular, descreve o uso de uma técnica semelhante que os ajudou a alcançar ordens de magnitude de economias potenciais. Outro blog que tenta medir os benefícios da otimização é este.

Aplicativos

  • Os Redis Hashes são naturalmente adequados para armazenar objetos:sessões, usuários, visitantes etc. Isso o torna uma das principais estruturas de dados fornecidas pelo Redis.
  • Em seu formato otimizado para memória, é uma excelente opção para armazenar em cache grandes quantidades de dados.

Um armazenamento de endereço de objeto

Como a otimização de memória é um caso de uso importante para hashes, vamos discutir um exemplo semelhante à implantação do Instagram para mostrar como utilizar os recursos de economia de memória dos hashes do Redis. Digamos que temos uma enorme implantação de armazenamento endereçável por conteúdo (CAS) com centenas de milhões de objetos armazenados. A localização de cada objeto é uma string de hash. Pretendemos desenvolver um sistema de pesquisa para descobrir a localização do objeto a partir de seu ID. A maneira típica de fazer isso no Redis será usar uma string.

set object:14590860 "007f80f0a62408..."
set object:11678 "009f80abcd0a60..."
...

Esta abordagem funciona muito bem. No entanto, como o número de objetos que temos é enorme, acabaremos precisando de muita memória para o Redis. Queremos fazer melhor. Vamos usar a abordagem de hash otimizada para memória para esse problema. Teremos que escolher os valores corretos para list-max-ziplist-entries e list-max-ziplist-value . O valor correto para list-max-ziplist-value é qualquer que seja o comprimento máximo da string de hash do endereço de armazenamento. O valor de list-max-ziplist-entries deve ser mantido baixo o suficiente e dependerá do número total de baldes de hash que desejamos criar. Será melhor descoberto empiricamente. Por exemplo para 100 milhões de objetos, poderíamos optar por usar 100k hashes. As entradas máximas nesse caso serão 100m / 100k =1000. A lógica do aplicativo para decidir em qual hash o endereço de armazenamento de um objeto deve ser:dividir o ID do objeto por 100k e descartar o restante. Assim, o ID do objeto 14590860 entrará em hash (14590860/100k) =145, ou seja,


hset object:145 14590860 "007f80f0a62408..."
hget object:145 14590860
> "007f80f0a62408..."

Esta implementação não será apenas muito mais leve na memória, mas também fornecerá uma boa localidade de cache.

Aqui estão nossas outras postagens na série de estrutura de dados do Redis.

  • Conjuntos Redis
  • Bitmaps do Redis
  • Conjuntos classificados do Redis