Некоторые дополнения к заданному набору ответов:
Прежде всего, если вы собираетесь эффективно использовать хэширование в Redis, вы должны знать максимальное количество ключей и максимальный размер значений - в противном случае, если они выдают значения из hash-max-ziplist-value или hash-max-ziplist-records, Redis преобразует его в практически обычные пары ключ / значение под капотом. (см. hash-max-ziplist-value, hash-max-ziplist-records). И ломать под капотом из опций хеш-функции очень плохо, потому что каждая обычная пара ключ / значение внутри Redis использует +90 байт на пару.
Это означает, что если вы начнете со второго варианта и случайно выйдете из max-hash-ziplist-value, вы получите +90 байт на КАЖДЫЙ АТРИБУТ, который вы имеете внутри пользовательской модели! (на самом деле не +90, а +70, см. вывод консоли ниже)
# 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
....
}
Для ответа TheHippo комментарии к Варианту 1 вводят в заблуждение:
hgetall / hmset / hmget на помощь, если вам нужны все поля или несколько операций get / set.
За ответ BMiner.
Третий вариант на самом деле очень интересный, для набора данных с max (id) <has-max-ziplist-value это решение имеет сложность O (N), потому что, к удивлению, Reddis хранит небольшие хэши в виде массива как контейнер длины / ключа / значения объекты!
Но много раз хеши содержат всего несколько полей. Когда хэши маленькие, мы можем вместо этого просто закодировать их в структуре данных O (N), как линейный массив с парами ключ-значение с префиксом длины. Поскольку мы делаем это только тогда, когда N мало, время амортизации для команд HGET и HSET по-прежнему равно O (1): хеш будет преобразован в реальную хеш-таблицу, как только число содержащихся в нем элементов будет слишком сильно расти.
Но вы не должны волноваться, вы очень быстро сломаете записи в hash-max-ziplist и приступите к решению № 1.
Второй вариант, скорее всего, пойдет к четвертому решению под капотом, потому что, как говорится в вопросе:
Имейте в виду, что если я использую хеш, длина значения не будет предсказуемой. Они не все короткие, как пример био выше.
И, как вы уже сказали: четвертое решение - самый дорогой +70 байт на каждый атрибут.
Мое предложение, как оптимизировать такой набор данных:
У вас есть два варианта:
Если вы не можете гарантировать максимальный размер некоторых пользовательских атрибутов, тогда вы выбираете первое решение, и если вопрос памяти имеет решающее значение, сожмите пользователя json перед сохранением в redis.
Если вы можете заставить максимальный размер всех атрибутов. Чем вы можете установить hash-max-ziplist-records / value и использовать хэши в виде одного хеша на представление пользователя ИЛИ в качестве оптимизации хеш-памяти из этого раздела руководства Redis: https://redis.io/topics/memory-optimization и сохранить пользователя в виде строки json. В любом случае вы также можете сжать длинные пользовательские атрибуты.