redis存大量键值对时内存占用优化

最近的项目里有个小需求,简单来说就是需要把app登陆后,每个设备(device_id)对应系统推送的enable/disable状态给记录下来。因为这个状态不需要与别的表进行join,只要能够提供一些get/set的快速查询功能就可以,所以定下来将这些数据存至redis
接下来就是思考数据结构了,如果数据量少的话,那怎么存都可以,简单的k-v对也能满足。但是这里涉及到所有的中国登陆用户,活跃量肯定会在百万级别以上,所以需要考虑到对内存占用作优化。之前我对几个数据结构内存存储这一块有个大概的认识,比较模糊,知道哪种方案好,但是不知道究竟好在哪里,能好多少。所以这次先多做几组测试比较,看看内存优化与不优化相差多大。


> 要存储这样的信息,可以采用的数据结构有两种:`string`,`hash` 。 string就是前文所说的,直接存kv对进去,每个k-v对在redis里都会是一个**sds(simple dynamic string)** struct, 内存占用是线性增长的,所以对几百万个key来说,这肯定是最占用内存的。 接下来是hash,hash是就是可以存k-v对的字典,相当于可以把这几百万个k-v对都存到一个**parent-key**下面,有底层**hashtable**的支持也可以减少内存的占用。但这里也引出了二个新的问题: 1. 如果几百万个k-v对都存到一个hash_key下面,那就会形成一个**big key**, big key在redis里是一个bad practise 2. redis本身提供了**ziplist**这样的数据结构,在hash中元素少于配置的`hash-max-ziplist-entries`时,就会采用ziplist来实现字典,ziplist要比hashtable省内存 这两个问题其实可以一起解决,创建很多的hash bucket,把key做一次mapping后,放进对应的hash bucket里,这样子既可以规避big key的问题,又可以使用ziplist,算是一个好的解决方案。 下面模拟**100万**的数据来做三个实验,redis版本是5.05,用python单线程生成uuid进行模拟写入 ###直接按string类型存放 跑完100万个key之后,info里关于memory的部分是这样的:
1
2
3
4
5
6
7
8
9
# Memory
used_memory:73243224
used_memory_human:69.85M
used_memory_rss:77819904
used_memory_rss_human:74.21M
used_memory_peak:73260176
used_memory_peak_human:69.87M
used_memory_peak_perc:99.98%
...

redis分配了69.85M内存

按hash类型存放,放到一个key下面

1
2
3
4
5
6
7
8
# Memory
used_memory:81243552
used_memory_human:77.48M
used_memory_rss:86122496
used_memory_rss_human:82.13M
used_memory_peak:81258640
used_memory_peak_human:77.49M
...

redis分配了77.48M内存

按hash类型,分bucket存放

1
2
3
4
5
6
7
used_memory:41935568
used_memory_human:39.99M
used_memory_rss:54710272
used_memory_rss_human:52.18M
used_memory_peak:81258640
used_memory_peak_human:77.49M
used_memory_peak_perc:51.61%

redis分配了39.99M内存

从上面可以看出,直接存hash和存string相差无几,对于这种存储需求和结构,一开始的估计不是很准确。第三种方案是最省内存的。

“Hashes, Lists, Sets composed of just integers, and Sorted Sets, when smaller than a given number of elements, and up to a maximum element size, are encoded in a very memory efficient way that uses up to 10 times less memory (with 5 time less memory used being the average saving)”