Bem, isso é esperado de qualquer armazenamento de dados eficiente:as palavras têm que ser indexadas na memória em uma estrutura de dados dinâmica de células ligadas por ponteiros. Tamanho dos metadados da estrutura, ponteiros e fragmentação interna do alocador de memória é a razão pela qual os dados ocupam muito mais memória do que um arquivo simples correspondente.
Um conjunto Redis é implementado como uma tabela de hash. Isso inclui:
- uma matriz de ponteiros crescendo geometricamente (potências de dois)
- uma segunda matriz pode ser necessária quando o rehashing incremental estiver ativo
- células de lista vinculada a um único que representam as entradas na tabela de hash (3 ponteiros, 24 bytes por entrada)
- Envolvedores de objetos Redis (um por valor) (16 bytes por entrada)
- dados reais (cada um deles prefixado por 8 bytes para tamanho e capacidade)
Todos os tamanhos acima são fornecidos para a implementação de 64 bits. Contabilizando a sobrecarga do alocador de memória, isso resulta no Redis levando pelo menos 64 bytes por item definido (em cima dos dados) para uma versão recente do Redis usando o alocador jemalloc (>=2.4)
O Redis fornece otimizações de memória para alguns tipos de dados, mas não abrange conjuntos de strings. Se você realmente precisa otimizar o consumo de memória dos conjuntos, existem truques que você pode usar. Eu não faria isso por apenas 160 MB de RAM, mas se você tiver dados maiores, aqui está o que você pode fazer.
Se você não precisar dos recursos de união, interseção e diferença de conjuntos, poderá armazenar suas palavras em objetos de hash. O benefício é que os objetos de hash podem ser otimizados automaticamente pelo Redis usando o zipmap se forem pequenos o suficiente. O mecanismo zipmap foi substituído por ziplist no Redis>=2.6, mas a ideia é a mesma:usar uma estrutura de dados serializada que possa caber nos caches da CPU para obter desempenho e um espaço de memória compacto.
Para garantir que os objetos de hash sejam pequenos o suficiente, os dados podem ser distribuídos de acordo com algum mecanismo de hash. Supondo que você precise armazenar 1 milhão de itens, adicionar uma palavra pode ser implementado da seguinte maneira:
- hash it módulo 10000 (feito no lado do cliente)
- Palavras HMSET:[hashnum] [palavra] 1
Em vez de armazenar:
words => set{ hi, hello, greetings, howdy, bonjour, salut, ... }
você pode armazenar:
words:H1 => map{ hi:1, greetings:1, bonjour:1, ... }
words:H2 => map{ hello:1, howdy:1, salut:1, ... }
...
Para recuperar ou verificar a existência de uma palavra, é o mesmo (hash e use HGET ou HEXISTS).
Com essa estratégia, uma economia significativa de memória pode ser feita desde que o módulo do hash seja escolhido de acordo com a configuração do zipmap (ou ziplist para Redis>=2.6):
# Hashes are encoded in a special way (much more memory efficient) when they
# have at max a given number of elements, and the biggest element does not
# exceed a given threshold. You can configure this limits with the following
# configuration directives.
hash-max-zipmap-entries 512
hash-max-zipmap-value 64
Cuidado:o nome desses parâmetros foi alterado com Redis>=2.6.
Aqui, módulo 10000 para 1M de itens significa 100 itens por hash objects, o que garantirá que todos eles sejam armazenados como zipmaps/ziplists.