我有一个处理已经转换成无符号长整数的键的系统(通过将短序列打包成字节串)。我想尝试将它们存储在Redis中,我希望尽可能以最佳方式进行。我关心的主要是记忆效率。
通过在线REPL播放,我注意到以下两个是相同的
zadd myset 1.0 "123"
zadd myset 1.0 123
这意味着即使我知道我想存储整数,也必须将其设置为字符串。我从文档中注意到,密钥只是存储为char*
,而SETBIT之类的命令表明Redis不反对将字符串视为客户端中的字节串。这暗示了存储unsigned long
s的效率略高于其字符串表示形式。
在排序集中存储unsigned long
的最佳方法是什么?
答案 0 :(得分:11)
感谢Andre的回答。以下是我的发现。
Redis键必须是字符串。如果要传递整数,则必须是某种字符串。对于小的,定义良好的值集,Redis会将字符串解析为整数(如果是1)。我的猜测是它会使用这个int来定制它的哈希函数(甚至可以根据值静态地维护哈希表)。这适用于较小的值(示例是64个条目的默认值,值最多为512)。我会在调查过程中测试更大的值。
http://redis.io/topics/memory-optimization
另一种方法是压缩整数,使其看起来像一个字符串。
看起来可以使用任何字节字符串作为键。
对于我的应用程序的情况,它实际上没有存储字符串或整数的那么大的差异。我想Redis中的结构无论如何都经历了某种对齐,所以无论如何可能会有一些预先浪费的字节。无论如何都要对值进行哈希处理。
使用Python进行测试,因此我可以使用struct.pack
创建值。 long long
的重量为8个字节,非常大。鉴于整数值的分布,我发现存储字符串实际上是有利的,特别是在以十六进制编码时。
由于redis字符串是“Pascal-style”:
struct sdshdr {
long len;
long free;
char buf[];
};
并且考虑到我们可以在那里存储任何东西,我做了一些额外的Python来将类型编码为最短的类型:
def do_pack(prefix, number):
"""
Pack the number into the best possible string. With a prefix char.
"""
# char
if number < (1 << 8*1):
return pack("!cB", prefix, number)
# ushort
elif number < (1 << 8*2):
return pack("!cH", prefix, number)
# uint
elif number < (1 << 8*4):
return pack("!cI", prefix, number)
# ulonglong
elif number < (1 << 8*8):
return pack("!cQ", prefix, number)
这似乎是一个微不足道的挽救(或根本没有)。可能是由于Redis中的struct padding。这也驱使Python CPU通过屋顶,使其有点没有吸引力。
我正在使用的数据是200000个z consecutive integer => (weight, random integer) × 100
,加上一些倒排索引(基于随机数据)。 dbsize
会产生1,200,001个密钥。
服务器的最终内存使用:1.28 GB RAM,1.32 Virtual。无论如何,各种调整都不会超过10兆字节。
所以我的结论是:
不要将编码麻烦为固定大小的数据类型。如果需要,只需将整数作为字符串存储在十六进制中。它不会产生那么大的差异。
参考文献:
答案 1 :(得分:1)
我不确定这个答案,它更像是一个建议而不是其他任何东西。我必须试一试,看看它是否有效。
据我所知,Redis仅支持UTF-8字符串。
我建议抓取你的长整数的位表示并相应地填充它以填充最近的字节。将每组8个字节编码为UTF-8字符串(以8x * utf8_char *字符串结尾)并将其存储在Redis中。他们未签名的事实意味着你不关心第一位,但如果你这样做,你可以在字符串中添加一个标志。
检索数据后,你必须记住再次将每个字符填充到8个字节,因为如果字符可以用较少的字节存储,UTF-8将使用较少的字节表示。
最终结果是存储最多8 x 8字节字符而不是(可能)最多64 x 8字节字符。