我想将JSON有效负载存储到redis中.我有两种方法可以做到这一点:
一个使用简单的字符串键和值.
key:user,value:payload(整个JSON blob,可以是100-200 KB)
SET user:1 payload
使用哈希
HSET user:1 username "someone"
HSET user:1 location "NY"
HSET user:1 bio "STRING WITH OVER 100 lines"
请记住,如果我使用哈希值,则值的长度是不可预测的.它们并非都是短的,例如上面的生物例子.
哪个内存更有效?使用字符串键和值,还是使用哈希?
BMi*_*ner 386
本文可以在这里提供很多见解:http://redis.io/topics/memory-optimization
有很多方法可以在Redis中存储一个Object数组(扰码:我喜欢大多数用例的选项1):
将整个对象作为JSON编码的字符串存储在单个键中,并使用集合(或列表,如果更合适)跟踪所有对象.例如:
INCR id:users
SET user:{id} '{"name":"Fred","age":25}'
SADD users {id}
Run Code Online (Sandbox Code Playgroud)
一般来说,这可能是大多数情况下最好的方法.如果对象中有很多字段,则对象不会与其他对象嵌套,并且您一次只能访问一小部分字段,最好选择选项2.
Advantages: considered a "good practice." Each Object is a full-blown Redis key. JSON parsing is fast, especially when you need to access many fields for this Object at once. Disadvantages: slower when you only need to access a single field.
Store each Object's properties in a Redis hash.
INCR id:users
HMSET user:{id} name "Fred" age 25
SADD users {id}
Run Code Online (Sandbox Code Playgroud)
Advantages: considered a "good practice." Each Object is a full-blown Redis key. No need to parse JSON strings. Disadvantages: possibly slower when you need to access all/most of the fields in an Object. Also, nested Objects (Objects within Objects) cannot be easily stored.
Store each Object as a JSON string in a Redis hash.
INCR id:users
HMSET users {id} '{"name":"Fred","age":25}'
Run Code Online (Sandbox Code Playgroud)
这允许您合并一点,只使用两个键而不是许多键.明显的缺点是你不能在每个用户对象上设置TTL(和其他东西),因为它只是Redis哈希中的一个字段而不是一个完整的Redis密钥.
优点:JSON解析速度很快,尤其是当您需要同时访问此Object的许多字段时.减少主要名称空间的"污染". 缺点:当你有很多对象时,大约与#1相同的内存使用量.当您只需要访问单个字段时,比#2慢.可能不被认为是"良好做法".
将每个Object的每个属性存储在专用键中.
INCR id:users
SET user:{id}:name "Fred"
SET user:{id}:age 25
SADD users {id}
Run Code Online (Sandbox Code Playgroud)
根据上面的文章,这个选项几乎从不首选(除非Object的属性需要特定的TTL或其他东西).
优点:对象属性是完整的Redis密钥,对您的应用程序来说可能不会过度. 缺点:速度慢,使用更多内存,而不是"最佳实践".很多污染主键名称空间.
Option 4 is generally not preferred. Options 1 and 2 are very similar, and they are both pretty common. I prefer option 1 (generally speaking) because it allows you to store more complicated Objects (with multiple layers of nesting, etc.) Option 3 is used when you really care about not polluting the main key namespace (i.e. you don't want there to be a lot of keys in your database and you don't care about things like TTL, key sharding, or whatever).
If I got something wrong here, please consider leaving a comment and allowing me to revise the answer before downvoting. Thanks! :)
The*_*ppo 150
这取决于您访问数据的方式:
选择方案1:
选择2:
PS:作为一个经验法则,请选择在大多数用例中需要较少查询的选项.
给定答案的一些补充:
首先,如果您要高效地使用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
....
}
Run Code Online (Sandbox Code Playgroud)
对于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字符串。无论哪种方式,您都可以压缩长用户属性。
| 归档时间: |
|
| 查看次数: |
125567 次 |
| 最近记录: |