对给定答案集的一些补充:
首先,如果你要有效地使用 Redis 哈希,你必须知道
键计数最大数量和值最大大小 - 否则,如果它们破坏了 hash-max-ziplist-value 或 hash-max-ziplist-entries,Redis 会将其转换为实际上常见的键/值对。 (请参阅 hash-max-ziplist-value, hash-max-ziplist-entries )并且从哈希选项中破解是非常糟糕的,因为 Redis 中的每个常用键/值对每对使用 +90 字节。
这意味着如果您从选项 2 开始并意外突破 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 的回答,选项一的 cmets 具有误导性:
如果您需要所有字段或多个 get/set 操作,hgetall/hmset/hmget 可以帮助您。
对于 BMiner 的回答。
第三个选项实际上非常有趣,对于 max(id)
但很多时候哈希只包含几个字段。当哈希值很小时,我们可以将它们编码为 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 字符串。无论哪种方式,您都可以压缩长用户属性。