redis数据结构与对象--常见问题

1. redis中5种数据类型的编码方式是什么?

①STRING(字符串对象):int、raw、embstr
int 编码:值可以用 long 类型保存的整数
raw 编码:值可以用 long double 类型保存的浮点数
embstr 编码:(只读,修改时需转化为 raw 类型)字符串值,或者是以为太长无法用 long 类型保存的整数,又或者因为太长无法用 long double 类型保存的浮点数。

②LIST(列表对象):ziplist、linkedlist
ziplist 编码:使用压缩列表作为底层实现。每个压缩列表结点存了一个列表元素。

  • 当列表同时满足以下条件,使用ziplist编码
    1. 列表对象保存的所有字符串元素长度都小于64字节。
    2. 列表对象保存的所有元素个数小于512个。

linklist 编码:使用双端链表作为底层实现。每个双端列表结点保存了一个字符串对象,而每个字符串对象都保存了一个列表元素。

③HASH(哈希对象):ziplist、hashtable
ziplist 编码:使用压缩列表作为底层实现。保存同一键值对的两个结点总是紧挨在一起的。

  • 当哈希对象同时满足以下条件,使用ziplist编码
    1. 哈希对象保存的所有字符串元素长度都小于64字节。
    2. 哈希对象保存的所有元素个数小于512个。

hashtable 编码:使用字典作为底层实现。每个键值对使用一个字典键值对保存。

④SET(集合对象):intset、hashtable
intset 编码:使用整数集合作为底层实现。

  • 当集合对象同时满足以下条件,使用intset编码
    1. 集合对象保存的所有元素都为整数值。
    2. 集合对象保存的所有元素个数小于512个。

hashtable 编码:使用字典作为底层实现。字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,字典的值都为NULL。

⑤SORT_SET(有序集合对象):ziplist、skiplist
ziplist 编码:使用压缩列表作为底层实现。每个元素集合使用两个紧挨在一起的节点来保存,一个保存元素的成员,一个保存元素的分值。

  • 当列表同时满足以下条件,使用ziplist编码
    1. 有序集合保存的所有字符串元素长度都小于64字节。
    2. 有序集合保存的所有元素个数小于128个。

skiplist 编码:使用zset作为底层实现,一个zset结构同时包含一个字典和一个跳跃表

2. 为什么redis不共享包含字符串的对象?

我们知道只有在共享对象和目标对象完全相同的情况下,程序才会将共享对象用作键的值对象,而一个共享对象保存的值越复杂,验证共享对象和目标对象是否完全相同的复杂度就越高,消耗CPU的时间会越多。

  • 如果共享对象保存整数值的字符串对象,复杂度为O(1)
  • 如果共享对象保存字符串值的字符串对象,复杂度为O(N)
  • 如果共享对象是包含多个值(或对象)的对象,复杂度为O(N²)

因此,redis 只对包含整数值的字符串对象进行共享。

对象的共享使用了引用计数属性

3. 为什么有序集合需要同时使用跳跃表和字典来实现?

同时使用比单独其中任意一种的性能都好,原因如下:

  1. 如果只是用字典来实现,那么虽然在查找成员时复杂度为O(1),但因为字典是无序保存元素的,当执行范围性操作例如 ZRANK 时,程序都需要进行排序。
  2. 如果只是使用跳跃表来实现,虽然执行范围性操作复杂度很低,当时在根据成员查找分值时因为没有了字典,复杂度会从O(1)变为O(longN)。

4. 有序集合同时使用跳跃表和字典会浪费内存吗?

字典和跳跃表会通过指针共享元素成员和分值,并不会造成任何的数据重复,也不会因此而浪费任何内存。

5. redis是怎样实现内存回收的?

因为C语言不具备内存回收功能,所以redis在自己的对象系统构建了一个引用计数技术来实现回收机制。

每个对象的引用计数信息由 redisobject 结构的 refcount 属性记录:

  • 在创建一个新对象时,引用计数值初始化为1
  • 当对象被一个新程序引用时,引用计数值+1
  • 当对象不再被一个程序引用时,引用计数值-1
  • 当对象的引用计数值变为0时,对象所占用的内存被释放

猜你喜欢

转载自blog.csdn.net/myjess/article/details/116148415