「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战」。
概念
Redis并没有直接使用数据结来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象、和有序集合对象这五种类型的对象,每种对象都用到了至少一种数据结构。
Redis的对象系统实现了基于引用计数技术的内存回收机制,当程序不再使用某个对象,这个对象占用的内存就会被自动释放,另外,Redis还通过引用计数技术实现了对象共享机制,通过这个共享机制,可以让多个数据库键共享同一个对象来节约内存。
对象类型和编码
Redis使用对象表示数据库的键和值,每次当我们在Redis的数据库中新创建一个键值对时,我们至少会创建两个对象,一个对象用来作为键值对的键,另一个对象用来作为键值对的值。
Redis每个对象都是由一个redisObject结构表示,该结构中和保存数据有关的三个属性如下图:
- 类型:对象type属性记录了对象的类型
Redis中,键总是一个字符串对象,而值则可能是字符串对象、列表对象、哈希对象、集合对象、或者有序集合对象这五个中的其中一个。
-
ptr:这个用来指向对象的底层数据结构
-
具体用哪种数据结构是由对象的encoding属性决定。
encoding属性记录了对象所使用的编码
通过encoding属性来设定对象使用的编码,而不是为特定类型对象关联一种固定的编码,极大提升了Redis的灵活性和效率,因为Redis可以根据不同的使用场景来为对象设置不同的编码,从而优化对象的使用效率。
例如我们提到的压缩列表和链表,当列表对象包含元素比较少的时候,Redis会使用压缩列表来作为列表对象的底层实现,当元素不断增多,这时候Redis会使用链表来作为底层数据结构。
字符串对象
字符串对象的编码可以是int、raw、embstr。
如果一个字符串对象保存的是一个整数值,并且这个整数值可以用long类型表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面。并将字符串对象的编码设置为int。
如果字符串对象保存的是一个字符串值,并且这个字符串值长度大于32字节,那么字符串对象将使用SDS来保存字符串值,并将对象编码设置为raw。
如果字符串对象保存的是一个字符串值,并且字符串值长度小于等于32字节,那么字符串对象就会使用embstr编码的方式来保存这个字符串值。
列表对象
列表对象的编码可以是ziplist或者linkedlist。
ziplist编码的列表对象使用压缩列表作为底层实现,每个压缩列表节点保存了一个列表元素。
linkedlist编码的列表使用双端列表作为底层实现,每个双端列表节点保存了一个字符串对象。
哈希对象
哈希对象的编码可以是ziplist或者hashtable。
- ziplist编码的哈希对象使用压缩列表作为底层实现,每当有新的键值对要加入到哈希对象时,程序会先将保存了键的压缩列表节点推入到列表表尾,因此同一键值对的两个节点总是紧挨在一起,保存键的节点在前,保存值的节点在后。
- hashtable编码的哈希对象使用字典作为底层实现,哈希对象中的每个键值对都使用一个字典键值对来保存
集合对象
集合对象编码可以是intset或者hashtable。
- intset编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合。
- hashtable编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而字典的值则全部被设置为NULL。
有序集合对象
有序集合编码可以是ziplist或者skiplist
- 使用ziplist,每个集合元素使用两个紧挨在一起的压缩列表节点来保存。第一个节点保存元素的成员对象,第二个节点保存元素的分值。
- 当使用跳跃表的结构,跳跃表会按照分值从小到大保存了所有集合元素。每个跳跃表节点都保存了一个集合元素。