我们项目使用Redis越来越多,可是Redis毕竟是基于内存的数据库,所以对于Redis的空间使用如果不加注意,很容易导致内存溢出。

1. Redis数据结构

Redis使用了五种数据对象:字符串对象,列表对象,哈希对象,集合对象,有序集合对象。

Redis基于这五种对象也给出了优化:

字符串对象

字符串中有int、embstr和raw(Simple Dynamic String)两种存储字符串的结构。如果只读且大小小于32字节,则使用embstr,否则使用raw,embstr只能转化为raw,raw不能转化为embstr。关于两者的区别详细了解Redis设计与实现

列表对象

列表对象的底层数据结构在3.2版本之前可以是链表(linkedlist),也可以是压缩列表(ziplist),对于链表保存不需要连续的内存空间(是优点也是缺点,容易造成内存碎片),相比于压缩列表多了保存pre、next指针的空间,ziplist保存在连续内存空间上,不需要维护pre、next指针。当保存的字符串长度小于64字节且列表对象个数小于512时使用压缩列表。

在3.2版本之后唯一的数据结构是快表(quicklist),它是一个双向链表,而且是一个ziplist的双向链表。这是什么意思呢?我们知道,双向链表是由多个节点(Node)组成的。这个描述的意思是:quicklist的每个节点都是一个ziplist。

双向链表便于在表的两端进行push和pop操作,但是它的内存开销比较大。首先,它在每个节点上除了要保存数据之外,还要额外保存两个指针;其次,双向链表的各个节点是单独的内存块,地址不连续,节点多了容易产生内存碎片。

ziplist由于是一整块连续内存,所以存储效率很高。但是,它不利于修改操作,每次数据变动都会引发一次内存的realloc。特别是当ziplist长度很长的时候,一次realloc可能会导致大批量的数据拷贝,进一步降低性能。

哈希对象

哈希对象底层数据结构可以是ziplist和字典hashtable。ziplist存储字符串不超过32字节和总个数不超过512的数据。

集合对象

集合对象底层数据结构是intset或者hashtable。

有序集合对象

有序集合对象底层数据结构是ziplist或者是skiplist。当存储的字符串长度大于64时转为skiplist。
skiplist存储时使用skiplist和hashtabl两种结构进行存储,如果我们只是用字典hashtable来实现有序集合,那么虽然已O(1)复杂度查找成员的分值这一特性被保留,但是字典以无序的方式保存集合元素,所以每次在执行范围操作时,都需要对字典保存的所有元素进行排序,完成这种排序至少需要O(NlogN)时间复杂度,以及额外的O(N)内存空间。同样如果只是用跳跃表,根据成员查找分值操作复杂度将为O(logN)。

需要注意的是字典和跳跃表会共享元素的成员和分值,并不会造成数据重复。

使用上述对象时,需要考虑底层数据结构特性,是否会因为频繁删除增加修改,造成Redis的内存碎片化,频繁修改的数据不建议放入到Redis。不仅如此,考虑到Redis空间的复用性,对于不常用的key设置过期时间。

2. Redis存储优化

本章节摘自《深入学习Redis(1):Redis内存模型 –编程迷思

  1. 利用Redis内存分配器做第一层优化。例如,如果key的长度如果是8个字节,则SDS为17字节,jemalloc分配32字节;此时将key长度缩减为7个字节,则SDS为16字节,jemalloc分配16字节;则每个key所占用的空间都可以缩小一半。

  2. 尽量使用整型/长整型。如果是整型/长整型,Redis会使用int类型(8字节)存储来代替字符串,可以节省更多空间。

  3. 利用共享对象,共享对象包括10000个整数,可以通过REDIS_SHARED_INTEGERS参数提高共享对象的个数。

  4. 关注内存碎片率,如果内存碎片率过高(jemalloc在1.03左右比较正常),说明内存碎片多,内存浪费严重;这时便可以考虑重启redis服务,在内存中对数据进行重排,减少内存碎片。

  5. 如果内存碎片率小于1,说明redis内存不足,部分数据使用了虚拟内存(即swap);由于虚拟内存的存取速度比物理内存差很多(2-3个数量级),此时redis的访问速度可能会变得很慢。因此必须设法增大物理内存(可以增加服务器节点数量,或提高单机内存),或减少redis中的数据。

  6. 要减少redis中的数据,除了选用合适的数据类型、利用共享对象等,还有一点是要设置合理的数据回收策略(maxmemory-policy),当内存达到一定量后,根据不同的优先级对内存进行回收。

参考资料

Redis中的列表对象(List)

深入学习Redis(1):Redis内存模型 –编程迷思

quicklist存储效率