开始学习redis,官网资料里讲了些内存优化的一些小技巧。
做了做笔记,并将最后一部分 Using hashes to abstract a very memory efficient plain key-value store on top of Redis 做了做实验验证一下,不验证觉着不爽。
思想:
存储kv时,可以将几个有公共前缀的key分成:公共前缀(key)+不同部分(field)。在数量较少时,会以线性数组压缩保存,可以节省空间。当数量大时会自动变为hash表。
比如,'object:1234'->value1, 'object:1256'->value2, 'object:3478'->value3,可以保存为:
object:12(key) 34(field) value1
56(field) value2
object:34(key) 78(field) value3
官网用ruby做客户端,试验了1w个kv: "object:0" ~ "object:10000" -> 'val'
UseOptimization set to true: 1.7 MB of used memory
UseOptimization set to false; 11 MB of used memory
以上结果是在以下阈值下成立的:
hash-max-zipmap-entries 256
hash-max-zipmap-value 1024
并指出当数量或大小超过指定阈值时,后者应该增加。
为了加深印象,我使用python写了小脚本测试:
测试数据不变,仍然为1w个kv: "object:0" ~ "object:10000" -> 'val'
代码:
import os
################################################################################
debug = 0
def system(cmd):
if debug:
print cmd
else:
os.system(cmd)
def re_put0(k, v):
system("./redis-cli set %s %s > /dev/null" % (k, v))
def re_put1(k, v):
tmp = k.split(':')
if len(tmp[1]) > 2:
hash_key = k[:9]
hash_field = k[9:]
else:
hash_key = "object:"
hash_field = tmp[1]
system("./redis-cli hset %s %s %s > /dev/null" % (hash_key, hash_field, v))
################################################################################
k = 'object:%s'
v = 'val'
for i in range(0, 10000):
re_put0(k % str(i), v)
system("./redis-cli info | grep used_memory_human")
system("./redis-cli flushall > /dev/null")
for i in range(0, 10000):
re_put1(k % str(i), v)
system("./redis-cli info | grep used_memory_human")
system("./redis-cli flushall > /dev/null")
一、
阈值为:
hash-max-zipmap-entries 256
hash-max-zipmap-value 1024
测试结果:
[arch@saber src]$ python2 testmem.py
used_memory_human:1.36M
used_memory_human:628.72K
二:
阈值为:
hash-max-zipmap-entries 20
hash-max-zipmap-value 1024
测试结果:
[arch@saber src]$ python2 testmem.py
used_memory_human:1.36M
used_memory_human:1.21M
可见,hash的内存占用有了显著提升。之所以内存占用依然比直接kv存储少,应该是少存了公共前缀的关系。
恩恩,实验的结果符合预期,心满意足。