文章目录
- 1. redis中5种数据类型的编码方式是什么?
- 2. 为什么redis不共享包含字符串的对象?
- 3. 为什么有序集合需要同时使用跳跃表和字典来实现?
- 4. 有序集合同时使用跳跃表和字典会浪费内存吗?
- 5. redis是怎样实现内存回收的?
1. redis中5种数据类型的编码方式是什么?
①STRING(字符串对象):int、raw、embstr
int 编码:值可以用 long 类型保存的整数
raw 编码:值可以用 long double 类型保存的浮点数
embstr 编码:(只读,修改时需转化为 raw 类型)字符串值,或者是以为太长无法用 long 类型保存的整数,又或者因为太长无法用 long double 类型保存的浮点数。
②LIST(列表对象):ziplist、linkedlist
ziplist 编码:使用压缩列表作为底层实现。每个压缩列表结点存了一个列表元素。
- 当列表同时满足以下条件,使用ziplist编码
- 列表对象保存的所有字符串元素长度都小于64字节。
- 列表对象保存的所有元素个数小于512个。
linklist 编码:使用双端链表作为底层实现。每个双端列表结点保存了一个字符串对象,而每个字符串对象都保存了一个列表元素。
③HASH(哈希对象):ziplist、hashtable
ziplist 编码:使用压缩列表作为底层实现。保存同一键值对的两个结点总是紧挨在一起的。
- 当哈希对象同时满足以下条件,使用ziplist编码
- 哈希对象保存的所有字符串元素长度都小于64字节。
- 哈希对象保存的所有元素个数小于512个。
hashtable 编码:使用字典作为底层实现。每个键值对使用一个字典键值对保存。
④SET(集合对象):intset、hashtable
intset 编码:使用整数集合作为底层实现。
- 当集合对象同时满足以下条件,使用intset编码
- 集合对象保存的所有元素都为整数值。
- 集合对象保存的所有元素个数小于512个。
hashtable 编码:使用字典作为底层实现。字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,字典的值都为NULL。
⑤SORT_SET(有序集合对象):ziplist、skiplist
ziplist 编码:使用压缩列表作为底层实现。每个元素集合使用两个紧挨在一起的节点来保存,一个保存元素的成员,一个保存元素的分值。
- 当列表同时满足以下条件,使用ziplist编码
- 有序集合保存的所有字符串元素长度都小于64字节。
- 有序集合保存的所有元素个数小于128个。
skiplist 编码:使用zset作为底层实现,一个zset结构同时包含一个字典和一个跳跃表。
2. 为什么redis不共享包含字符串的对象?
我们知道只有在共享对象和目标对象完全相同的情况下,程序才会将共享对象用作键的值对象,而一个共享对象保存的值越复杂,验证共享对象和目标对象是否完全相同的复杂度就越高,消耗CPU的时间会越多。
- 如果共享对象保存整数值的字符串对象,复杂度为O(1)
- 如果共享对象保存字符串值的字符串对象,复杂度为O(N)
- 如果共享对象是包含多个值(或对象)的对象,复杂度为O(N²)
因此,redis 只对包含整数值的字符串对象进行共享。
对象的共享使用了引用计数属性
3. 为什么有序集合需要同时使用跳跃表和字典来实现?
同时使用比单独其中任意一种的性能都好,原因如下:
- 如果只是用字典来实现,那么虽然在查找成员时复杂度为O(1),但因为字典是无序保存元素的,当执行范围性操作例如 ZRANK 时,程序都需要进行排序。
- 如果只是使用跳跃表来实现,虽然执行范围性操作复杂度很低,当时在根据成员查找分值时因为没有了字典,复杂度会从O(1)变为O(longN)。
4. 有序集合同时使用跳跃表和字典会浪费内存吗?
字典和跳跃表会通过指针共享元素成员和分值,并不会造成任何的数据重复,也不会因此而浪费任何内存。
5. redis是怎样实现内存回收的?
因为C语言不具备内存回收功能,所以redis在自己的对象系统构建了一个引用计数技术来实现回收机制。
每个对象的引用计数信息由 redisobject 结构的 refcount 属性记录:
- 在创建一个新对象时,引用计数值初始化为1
- 当对象被一个新程序引用时,引用计数值+1
- 当对象不再被一个程序引用时,引用计数值-1
- 当对象的引用计数值变为0时,对象所占用的内存被释放