Деякі доповнення до заданого набору відповідей:
Перш за все, якщо ви збираєтесь ефективно використовувати хеш Redis, ви повинні знати, що ключі підраховують максимальне число та значення max size - інакше, якщо вони вийдуть з хеш-макс-ziplist-значення або хеш-макс-ziplist-записи, Redis перетворить його практично звичайні пари ключів / значень під кришкою. (див. хеш-макс-ziplist-значення, хеш-макс-ziplist-записи) І пробивання під кришкою з параметрів хешу дійсно БАЖЕ, оскільки кожна звичайна пара ключів / значень всередині Redis використовує +90 байт на пару.
Це означає, що якщо ви почнете з другого варіанту і випадково вирветеся з max-hash-ziplist-значення, ви отримаєте +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, коментарі до Варіанту перший вводять в оману:
hgetall / hmset / hmget на допомогу, якщо вам потрібні всі поля або кілька операцій get / set.
Для відповіді BMiner.
Третій варіант насправді цікавий, оскільки для набору даних з max (id) <has-max-ziplist-value це рішення має складність O (N), тому що, дивно, Reddis зберігає невеликі хеші як контейнер, що нагадує масив довжиною / ключем / значенням об’єкти!
Але багато разів хеші містять лише кілька полів. Коли хеші невеликі, ми можемо замість цього просто зашифрувати їх у структурі даних O (N), як лінійний масив із встановленими по довжині парами ключових значень. Оскільки ми робимо це лише тоді, коли N мале, амортизований час для команд HGET і HSET все ще O (1): хеш буде перетворений у справжню хеш-таблицю, як тільки кількість елементів, які він містить, зросте занадто багато
Але ви не повинні турбуватися, ви швидко розібнете записи хеш-макс-ziplist і там ви вже перебуваєте на рівні рішення №1.
Другий варіант, швидше за все, піде на четверте рішення під кришкою, оскільки, як йдеться в питанні:
Майте на увазі, що якщо я використовую хеш, довжина значення не передбачувана. Вони не всі короткі, як, наприклад, біодоклад вище.
І як ви вже говорили: четверте рішення - це найдорожчий +70 байт на кожен атрибут точно.
Моя пропозиція, як оптимізувати такий набір даних:
У вас є два варіанти:
Якщо ви не можете гарантувати максимальний розмір деяких атрибутів користувача, тоді ви перейдете до першого рішення, і якщо значення пам'яті є вирішальним, ніж стиснути json користувача перед зберіганням у redis.
Якщо ви можете застосувати максимальний розмір усіх атрибутів. Тоді ви можете встановити хеш-max-ziplist-записи / значення та використовувати хеші як один хеш на представлення користувача АБО як оптимізацію хеш-пам'яті з цієї теми керівництва Redis: https://redis.io/topics/memory-optimization та зберігати користувача як рядок json У будь-якому випадку ви також можете стискати довгі атрибути користувача.