给定答案集的一些补充:
首先,如果您要有效地使用 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 个解决方案。
第二个选项很可能会转到引擎盖下的第四个解决方案,因为正如问题所述:
请记住,如果我使用哈希,则值长度是不可预测的。它们并不都是短的,例如上面的生物示例。
正如您已经说过的:第四个解决方案肯定是每个属性最昂贵的 +70 字节。
我的建议如何优化此类数据集:
你有两个选择:
如果你不能保证某些用户属性的最大大小,那么你会选择第一个解决方案,如果内存很重要,那么在存储到 redis 之前压缩用户 json。
如果您可以强制所有属性的最大大小。然后,您可以设置 hash-max-ziplist-entries/value 并使用散列作为每个用户表示的一个散列或作为 Redis 指南的这个主题的散列内存优化: https ://redis.io/topics/memory-optimization和将用户存储为 json 字符串。无论哪种方式,您也可以压缩长用户属性。