Redis数据结构原理
- Redis数据结构原理
- String
- String有三种编码方式: 1.RAW编码:基于简单动态字符串实现 2.EMBSTR编码 3.INT编码
- RAW,基于简单动态字符串(SDS)实现,存储上限为512mb。
 - 如果存储的SDS长度小于44字节(RedisObject+SDS的长度加起来刚好是64,这样做的好处是不会产生内存碎片),则会采用EMBSTR编码,此时RedisObject与SDS是一段连续空间。申请内存时只需要调用一次内存分配函数(RAW编码需要调用两次内存分配函数,先申请RedisObject再申请SDS的)
 - 如果存储的字符串是整数值,并且大小在LONG_MAX(8字节)范围内,则会采用INT编码:直接将数据保存在RedisObject的ptr指针位置(刚好8字节),不再需要SDS的参与了
- long型还有的特点是: 在对string进行incr(+1), decr(-1)等操作的时候 ● 如果它内部是OBJ_ENCODING_INT编码,那么可以直接行加减操作; ● 如果它内部是OBJ_ENCODING_RAW或OBJ_ENCODING_EMBSTR编码,那么Redis会先试图把字符串转成long型,如果能转成功,再进行加减操作。 如果对⼀个内部表示成long型的string执行append, setbit, getrange这些命令,针对的仍然是string的值。比如字符串”32”,如果按照字符数组来解释,它包含两个字符,它们的ASCII码分别是0x33和0x32 。当我们执行命令setbit key 7 0的时候,相当于把字符0x33变成了0x32,这样字符串的值就变成了”22”。⽽如果将字符串”32”按照内部的64位long型来解释,那么它是0x0000000000000020,在这个基础上执⾏setbit位操作,结果就跟之前的不一样了。因此,在这些命令的实现中,会把long型先转成字符串再进行相应的操作。
 
 
 
 - String有三种编码方式: 1.RAW编码:基于简单动态字符串实现 2.EMBSTR编码 3.INT编码
 - List
- ● 在3.2版本之前,Redis采用ZipList或者LinkedList来实现List,当元素数量小于512并且元素大小小于64字节时采用ZipList编码,超过则采用LinkedList编码。 ● 在3.2版本之后,Redis统一采用QuickList来实现List
 
 - Set
- Set会在不同情况下采用不同的编码: ● 当存储的所有数据都是整数,并且元素数量不超过set-max-intset-entries时,Set会采用IntSet编码,以节省内存 ● 否则就采用HT进行编码(也就是Dict),key用来存储元素,value设置为null 并且这两种编码可以进行转换
 
 - ZSet
- ZSet也就是SortedSet,其中每一个元素都需要指定一个score值和member值(set中member值唯一),所以这个数据结构需要满足kv存储、key唯一、可排序 所以就有下面两种选择 ● ZipList ● HT+SkipList
- ZipList情况: 当元素数量不多时,zset会采用ZipList结构(它不能存储太多元素,所以要满足下面的条件)来节省内存,不过需要同时满足两个条件: ● 元素数量小于zset_max_ziplist_entries,默认值128 ● 每个元素都小于zset_max_ziplist_value字节,默认值64 跟上面提到的Set数据结构一样,ZipList和HT+SkipList也可以进行相互转换
- 为什么可以选择ZipList呢?他既不可以保证有序,又不保证键唯一,又不满足键值存储!!! 为啥选择他!!! 答案是因为他内存占用比较小,这些功能可以由Redis编码然后进行实现:比如他实现键值存储就是用相邻的两个entry,一个当作key,一个当成value。排序是:每次添加就去entrys里面去找,添加的时候按顺序添加和唯一添加就好了。 但是你会说,这种性能比hash差啊,其实也不会差多少,这是有原因的,因为这种情况数据量小,所以这些遍历链表进行排序或者插入的业务不会特别影响性能,但是如果数据量大的话,就会很影响了,但是数据量大的话我们就不采用这种方式了,就换成另一种方式了。
 
 - HT+SkipList的情况: ● SkipList:可以排序,并且可以同时存储score和ele值(member)(但是不满足key唯一和根据key找值) ● HT(Dict):可以键值存储,并且可以根据key找value(无法排序) 可以发现,这两个单个都无法满足我们的需求,所以redis底层是把这两种数据结构进行组合进行使用的。那他是怎么组合的呢,他是把数据进行存储了两份,用双重的内存占用来换取功能。
 
 - ZipList情况: 当元素数量不多时,zset会采用ZipList结构(它不能存储太多元素,所以要满足下面的条件)来节省内存,不过需要同时满足两个条件: ● 元素数量小于zset_max_ziplist_entries,默认值128 ● 每个元素都小于zset_max_ziplist_value字节,默认值64 跟上面提到的Set数据结构一样,ZipList和HT+SkipList也可以进行相互转换
 
 - ZSet也就是SortedSet,其中每一个元素都需要指定一个score值和member值(set中member值唯一),所以这个数据结构需要满足kv存储、key唯一、可排序 所以就有下面两种选择 ● ZipList ● HT+SkipList
 - Hash
- Hash结构与Redis中的Zset非常类似: ● 都是kv存储 ● key唯一 区别如下: ● ZSet的键是member,值是score;Hash的键和值都是任意值 ● ZSet要根据score排序;Hash则无需排序 所以可以看出来这个Hash和ZSet超级像,所以他们的底层采用的编码也基本一致,只需要把有关排序的SkipList给去掉就行了 所以Hash的底层实现方式: ● ZipList ● HT
- 当Hash中数据项比较少的情况下,Hash底层采⽤压缩列表ZipList进⾏存储数据
 - 随着数据的增加,底层的ziplist就可能会转成dict,当满足下面条件之一就会进行转换。 ○ ziplist中的元素数量 hash-max-ziplist-entries 512 ○ ziplist中的任意entry的大小 hash-max-ziplist-value 64
 
 
 - Hash结构与Redis中的Zset非常类似: ● 都是kv存储 ● key唯一 区别如下: ● ZSet的键是member,值是score;Hash的键和值都是任意值 ● ZSet要根据score排序;Hash则无需排序 所以可以看出来这个Hash和ZSet超级像,所以他们的底层采用的编码也基本一致,只需要把有关排序的SkipList给去掉就行了 所以Hash的底层实现方式: ● ZipList ● HT
 
 - String
 
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 是小白菜哦!

