读《redis设计与实现》笔记--redis数据结构

redis五大数据结构:string,hash,list,set,zset(有序集合)

redis底层数据结构:简单动态字符串(SDS),链表,字典,跳表,整数集合,压缩列表

底层数据结构详解:

1.简单动态字符串:类似于c的结构体,但是SDS拥有记录已用长度(len)与剩余空间长度(free),当空间不足时会进行扩容。SDS最后会保存一个空字符

所以SDS获取字符串长度的时间复杂度为o(1),SDS自动扩容也不会像C一样产生溢出

利用未使用空间,SDS拥有空间预分配和惰性空间释放两种优化方法。

空间预分配:

修改SDS之后之后,如果len<1MB,将free修改为和len一样的长度,如果len>=1MB,将free修改为1MB。

例如:str = "redis"

如果拼接str与"cluster",那么修改之后len为5+6=11,11+11+1=23,len/free值都是11,加上最后一个空字符,总长为23。

要是str修改之后大小为20MB,那扩容之后就是20MB+1MB+1byte

将扩容需要的次数从最多n次变为至多n次。

惰性空间释放:

不会真正释放内存大小,例如删除redis中的字符e与d,那么str就变为ris,但是free=2。

这样减少释放空间的操作,将来扩容也会减少操作。

tips:SDS有专门的api可以真正释放空间,不用担心惰性释放带来空间的浪费。

二进制安全

C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。

因此,为了确保Redis可以适用于各种不同的使用场景,SDS的API都是二进制安全的。

SDS和c的区别:

 

2.链表

应用:列表键的底层实现之一就是链表。当一个列表键包含了数量比较多的元素,又或者列表中包含的元素都是比较长的字符串时,Redis就会使用链表作为列表键的底层实现。

除了链表键之外,发布与订阅、慢查询、监视器等功能也用到了链表,Redis服务器本身还使用链表来保存多个客户端的状态信息,以及使用链表来构建客户端输出缓冲区。

一个链表结点:

链表:

3.字典(key-value)

字典在Redis中的应用相当广泛,比如Redis的数据库就是使用字典来作为底层实现的,对数据库的增、删、查、改操作也是构建在对字典的操作之上的。

字典还是哈希键的底层实现之一,当一个哈希键包含的键值对比较多,又或者键值对中的元素都是比较长的字符串时,Redis就会使用字典作为哈希键的底层实现。

Redis的字典使用哈希表作为底层实现,而每个哈希表节点就保存了字典中的一个键值对。

字典所用的哈希表结构:

每个dictEntry结构保存着一个键值对

dictEntry结构:

字典结构:

type属性是一个指向dictType结构的指针,每个dictType结构保存了一簇用于操作特定类型键值对的函数,Redis会为用途不同的字典设置不同的类型特定函数。

privdata属性则保存了需要传给那些类型特定函数的可选参数。

Redis的哈希表使用链地址法来解决键冲突。

扩展和收缩哈希表的工作可以通过执行rehash(重新散列)操作来完成。

Redis Bgsave 命令用于在后台异步保存当前数据库的数据到磁盘。

对哈希表进行扩展:1.正在执行bgsave,负载因子>=5      2.没有执行bgsave,负载因子>=1

对哈希表进行收缩:负载因子<0.1

渐进式hash:rehash动作并不是一次性、集中式地完成的,而是分多次、渐进式地完成的。

原因:如果哈希表里保存的键值对数量千万甚至亿个键值对,那么要一次性将这些键值对全部rehash的话,庞大的计算量可能会导致服务器在一段时间内停止服务。

所以rehash期间,更新,删除,查找在两个表里进行,旧表查找不到的话去新表查找。

而插入就直接在新表进行,所以旧表只减不增。

字典使用哈希表作为底层实现,每个字典带有两个哈希表,一个平时使用,另一个仅在进行rehash时使用。

4.跳跃表

Redis使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员是比较长的字符串时,Redis就会使用跳跃表来作为有序集合键的底层实现。

Redis只在两个地方用到了跳跃表,一个是实现有序集合键,另一个是在集群节点中用作内部数据结构。

每个节点都有向前,向后的指针,向后的可能有多个(看有多少层有这个数据),向前的只有一个。

插入方法:从底层开始,每一层选择插入或者不插入,插入一层的话就停止。

所以第一层的插入概率1/2,第二次1/4...

5.整数集合

整数集合是集合键的底层实现之一。

整数集合的结构:

contents数组各个项在数组中按值的大小从小到大有序地排列,并且数组中不包含任何重复项。contents数组的真正类型取决于encoding属性的值。

升级:

每当我们要将一个新元素添加到整数集合里面,并且新元素的类型比整数集合现有所有元素的类型都要长时,整数集合需要先进行升级,然后才能将新元素添加到整数集合里面。

升级步骤:1.根据新数据类型,扩展底层数组大小 2.将其他数据设置为新数据类型 3.新元素添加到底层数组

升级好处:一个是提升整数集合的灵活性,另一个是尽可能地节约内存。

节约内存:有int_16,int_32,int_64三种类型,整数集合现在的做法既可以让集合能同时保存三种不同类型的值,又可以确保升级操作只会在有需要的时候进行,这可以尽量节省内存。

没有降级操作。

6.压缩列表

压缩列表是列表键和哈希键的底层实现之一。

一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,就会用压缩列表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值