给定答案的一些补充:
首先,如果您要高效地使用Redis哈希,则必须知道键计数的最大数量和值的最大大小-否则,如果它们破坏了hash-max-ziplist-value或hash-max-ziplist-entries,Redis会将其转换为实际值常用的键/值对。(请参见hash-max-ziplist-value,hash-max-ziplist-entries),从哈希选项中解脱确实是很糟糕的,因为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的答案,对选项一的评论具有误导性:
如果需要所有字段或多个get / set操作,请使用hgetall / hmset / hmget进行救援。
对于BMiner的答案。
第三种选择实际上真的很有趣,对于max(id)<has-max-ziplist-value的数据集,此解决方案具有O(N)复杂度,因为令人惊讶的是,Reddis将小的散列存储为长度/键/值的数组式容器对象!
但是很多时候,哈希仅包含几个字段。当哈希较小时,我们可以将其编码为O(N)数据结构,例如带有长度前缀键值对的线性数组。由于我们仅在N较小时执行此操作,因此HGET和HSET命令的摊销时间仍为O(1):一旦包含的元素数量过多,哈希将转换为真实的哈希表
但是您不必担心,您将很快破坏hash-max-ziplist-entries,然后您实际上就已经在解决方案编号1上了。
第二种选择很可能在第四个解决方案下获得解决,因为有疑问指出:
请记住,如果使用散列,则值长度是不可预测的。它们并不都是短的,例如上面的bio示例。
就像您已经说过的那样:第四个解决方案是每个属性最昂贵的+70字节。
我的建议是如何优化此类数据集:
您有两种选择:
如果您不能保证某些用户属性的最大大小,则可以使用第一个解决方案;如果内存问题至关重要,则可以在存储到Redis中之前压缩用户json。
如果可以强制所有属性的最大大小。比起您可以设置hash-max-ziplist-entries / value并将散列作为每个用户表示形式的一个散列,或作为Redis指南的以下主题中的散列内存优化来使用:https : //redis.io/topics/memory-optimization和将用户存储为json字符串。无论哪种方式,您都可以压缩长用户属性。