Algumas adições a um determinado conjunto de respostas:
Antes de tudo, se você usar o hash Redis de maneira eficiente, deverá saber que as teclas contam número máximo e valores tamanho máximo - caso contrário, se elas quebrarem as entradas hash-max-ziplist-value ou hash-max-ziplist-redis, o Redis o converterá em praticamente pares chave / valor usuais sob um capô. (consulte as entradas hash-max-ziplist, hash-max-ziplist) E quebrar sob o capô de uma opção de hash É MUITO RUIM, porque cada par de chave / valor usual dentro do Redis usa +90 bytes por par.
Isso significa que, se você começar com a opção dois e acidentalmente romper o valor de max-hash-ziplist, receberá +90 bytes por CADA ATRIBUTO que tiver dentro do modelo do usuário! (na verdade, não os +90, mas +70, veja a saída do console abaixo)
# you need me-redis and awesome-print gems to run exact code
redis = Redis.include(MeRedis).configure( hash_max_ziplist_value: 64, hash_max_ziplist_entries: 512 ).new
=> #<Redis client v4.0.1 for redis://127.0.0.1:6379/0>
> redis.flushdb
=> "OK"
> ap redis.info(:memory)
{
"used_memory" => "529512",
**"used_memory_human" => "517.10K"**,
....
}
=> nil
# me_set( 't:i' ... ) same as hset( 't:i/512', i % 512 ... )
# txt is some english fictionary book around 56K length,
# so we just take some random 63-symbols string from it
> redis.pipelined{ 10000.times{ |i| redis.me_set( "t:#{i}", txt[rand(50000), 63] ) } }; :done
=> :done
> ap redis.info(:memory)
{
"used_memory" => "1251944",
**"used_memory_human" => "1.19M"**, # ~ 72b per key/value
.....
}
> redis.flushdb
=> "OK"
# setting **only one value** +1 byte per hash of 512 values equal to set them all +1 byte
> redis.pipelined{ 10000.times{ |i| redis.me_set( "t:#{i}", txt[rand(50000), i % 512 == 0 ? 65 : 63] ) } }; :done
> ap redis.info(:memory)
{
"used_memory" => "1876064",
"used_memory_human" => "1.79M", # ~ 134 bytes per pair
....
}
redis.pipelined{ 10000.times{ |i| redis.set( "t:#{i}", txt[rand(50000), 65] ) } };
ap redis.info(:memory)
{
"used_memory" => "2262312",
"used_memory_human" => "2.16M", #~155 byte per pair i.e. +90 bytes
....
}
Para a resposta TheHippo, os comentários sobre a opção um são enganosos:
hgetall / hmset / hmget para o resgate, se você precisar de todos os campos ou de várias operações get / set.
Para resposta BMiner.
A terceira opção é realmente muito divertida, para um conjunto de dados com valor máximo (id) <has-max-ziplist-value, esta solução possui complexidade O (N), porque, surpresa, o Reddis armazena pequenos hashes como contêineres de comprimento / chave / valor em forma de matriz objetos!
Mas muitas vezes os hashes contêm apenas alguns campos. Quando os hashes são pequenos, podemos apenas codificá-los em uma estrutura de dados O (N), como uma matriz linear com pares de valores-chave com prefixo de comprimento. Como fazemos isso apenas quando N é pequeno, o tempo amortizado para os comandos HGET e HSET ainda é O (1): o hash será convertido em uma tabela de hash real assim que o número de elementos que ele contiver aumentar demais
Mas não se preocupe, você quebrará as entradas hash-max-ziplist muito rápido e lá está você agora na solução número 1.
A segunda opção provavelmente irá para a quarta solução, porque, como afirma a pergunta:
Lembre-se de que, se eu usar um hash, o tamanho do valor não será previsível. Eles não são todos curtos, como o exemplo bio acima.
E como você já disse: a quarta solução é o mais caro +70 bytes por cada atributo, com certeza.
Minha sugestão de como otimizar esse conjunto de dados:
Você tem duas opções:
Se você não puder garantir o tamanho máximo de alguns atributos do usuário, procure a primeira solução e se a questão da memória for crucial, comprima o usuário json antes de armazenar em redis.
Se você pode forçar o tamanho máximo de todos os atributos. Você pode definir hash-max-ziplist-entradas / valor e usar hashes como um hash por representação do usuário OU como otimização de memória de hash neste tópico de um guia Redis: https://redis.io/topics/memory-optimization e armazenar usuário como string json. De qualquer forma, você também pode compactar atributos de usuário longos.