Redis数据结构与对象
参考《Redis设计与实现》
一丶简单动态字符串
当redis需要的不仅仅是一个字符串字面量,而是一个可以被修改的字符串值时,就会使用SDS(simple dynamic string)来表示字符串值。比如set msg "hello world"
将创建一个新键值对,键值对的键是一个字符串对象(存储着msg),值也是一个字符串对象(存储者hello world)
1.SDS的结构
- free属性记录buf数组剩余未使用的字节数量
- len属性记录当前buf数据已经使用的字符数量
- buf属性是char类型的数组,最后一个字节保存空字符
\0
2.SDS的优点
2.1 常数时间复杂度获取字符串长度
传统C语言的字符串,需要遍历整个字符串遇到字符串结尾的\0
结束计数,但是SDS的len属性便记录了字符串的长度,可以常数时间获取字符串长度
2.2杜绝缓冲区溢出
传统C语言修改字符串可能导致缓冲区溢出(多个字符串相邻的时候,修改到了相邻位置的其他字符串)但是SDS进行修改的时候,会先检查SDS空间是否满足修改需要的要求,如果不满足九自动扩容到需要的大小,然后才执行修改操作
2.3减少修改字符串带来的内存重分配次数
SDS实现了空间预分配和惰性空间释放
空间预分配
如果对SDS修改后,SDS的长度小于1mb(len属性)那么程序分配和len相同大小的未使用空间。如果SDS修改后长度大于1mb那么程序分配1mb大小的未使用空间。空间预分配减少连续执行字符串操作需要的内存分配次数。
惰性空间释放
当SDS进行字符串缩操作的时候,并不会立即将不需要的空间进行内存重分配,而是修改free属性进行记录。
二丶链表
链表在redis中始于广泛,当前列表键包含了较多元素,又或者包含的元素都是较长的字符串的时候,redis将始于链表作为列表键(xx键表示键对应的值是xx类型)的实现。
发布订阅,慢查询等功能就是基于链表实现的
1.链表结构
2.链表的优点
- 双端,获取某个节点的前置后置都是常数时间复杂度
- 无环
- 带表头指针和,表尾指针
- 带链表长度计数器
- 多态,链表节点使用void*指针保存节点值
三丶字典
字典是一种用于保存键值对的数据结构,一个key可以和一个value进行关联。
字典在redis中使用广泛,redis数据库就是使用字典作为底层实现的,对数据库的增删改查都是构建在字典这种数据结构之上,字典还是哈希键的底层实现,当哈希键包含的键值对比较多,又或者键值对中的元素都是较长的字符串是,redis使用字典作为哈希键的底层实现。
1.字典的结构
可以看到redis的字典使用拉链法解决哈希冲突,一个字典存在两个dictht,一个用于存储数据,一个用于渐进式rehash
2.哈希算法
redis使用MurmurHash2算法计算key的hash值,然后将hash值于sizemask进行且操作,相当于一次对数组大小的取模,可以得到当前key应该落在哈希表数组的那个下标位置
3.解决hash冲突
redis使用拉链发来解决hash冲突,每一个哈希节点具备一个next节点,多个哈希节点使用next指针串联成单向链表,从而解决hash冲突的问题
4.渐进式rehash
随着操作不断进行,哈希表可能存储很多数据,为了让哈希表的负载因子维持在一个合理的范围,当哈希表保存的键值太多的时候,程序需要对哈希表的大小进行相应的扩展或者收缩。
4.1渐进式rehash的步骤
- 为ht[1]分配空间
- 字典中维护一个索引计数器rehashidx,设置为0,表示渐进式rehash正式开始
- 在rehash的期间,对字典进行的增删改查,除了完成迁移哈希数组中的内容到ht[1]之外,还会将顺带将rehashidx索引上的所有键值对rehash到ht[1],然后将rehashidx自增1
- 随着字典操作的不断进行,最终会完全rehash完ht[0]中的所有元素,rehashidx置为-1,表示结束
4.2渐进式rehash期间哈希表的使用
由于渐进式rehash的期间,字典具备两个哈希表,字典的增删改查都需要在两个哈希表中进行,如果ht[0]不存在数据,还需要去ht[1]中寻找,
4.3哈希表扩容或者收缩的前提
当下列条件中满足任意一个的时候,程序会自动进行哈希表的扩容
- 服务器没有执行BGSAVE(RDB持久化),或者BGREWRITEAOF(AOF持久化)并且哈希表负载因子大于等于1
- 服务器正在执行BGSAVE(RDB持久化),或者BGREWRITEAOF(AOF持久化)但是哈希表负载因子大于等于5
负载因子 = 哈希表存储的节点数量 / 哈希表大小
BGSAVE,或者BGREWRITEAOF进行的途中,进来不进行rehash的原因是,这两个命令进行的过程中,redis需要创建服务器子进程,采用写时复制的技术优化子进程的使用效率,避免子进程运行的途中进行rehash可以节约内存
当负载因子小于0.1的时候,redis会对哈希表进行收缩
四丶跳跃表
跳跃表是一种有序的数据结构,支持O(log N)时间复杂度进行节点查找。
redis使用跳跃表作为有序集合键的底层实现之一,如果有序集合包含的元素,或者有序集合中元素的成员都是较长的字符串的时候,redis使用跳跃表作为有序集合键的底层实现。此外集群节点中也了使用跳表。
1.跳跃表的结构
2.跳跃表中的分值和成员
跳跃表是有序的结构,其中的分值便是排序的依据,多个节点可以包含相同的分值,分值相同的时候根据节点保存对象的大小进行排序,每个节点保存的对象必须唯一
五丶整数集合
整数集合是集合键的底层实现之一,当一个集合只有整数元素,且集合元素不多的适合,redis使用整数集合作为集合键底层实现
1.整数集合的结构
2.整数集合encoding编码方式
属性值表示contents数组中,整数的类型是int8_t,int16_t,int32_t,还是int64_t。
3.升级
当一个新元素添加到整数集合中,并且新元素的类型比整数集合中其他元素的类型都要长时,整数集合会进行升级,然后把新元素添加到集合中。升级的步骤:
- 根据新元素的类型,扩展整数集合底层数组的空间大小,并且为新元素分配空间
- 底层contents元素的类型转换到新元素相同类型,并放到争取的位置上,有序性不变
- 新元素添加到contents数组中
升级的好处:
提升灵活性
整数集合可以通过升级保存不同类型的新元素
节约内存
在需要的适合才会升级,才需要更大的内存空间,可以减少内存的占用
整数集合,不会进行降级。
六丶压缩列表
压缩列表ziplist是列表建和哈希键的底层实现之一。
当一个列表只包含少量列表项的,并且每一个列表项是小整数或者长度段的字符串,redis使用压缩列表作为列表键的底层实现(相比于链表,少前继后继指针更加节约内存)
当一个哈希键只包含少量键值对的适合,并且每个键值对的键和值都是小整数,或者段字符串的适合,redis使用压缩列表作为哈希键的底层实现
1.压缩列表的结构
2.连锁更新
每一个节点的previous_entry_length记录了前一个节点的长度,如果前一个节点的长度小于254字节,那么此属性使用一个字节进行记录,如果大于254字节那么使用五字节进行记录,所有如果新的节点的插入,也许这个节点的长度大于1字节,那么其后面的节点需要更新previous_entry_length为5字节大小,可能导致后续的节点也需要更新previous_entry_length,引发连锁更新
七丶对象
前面我们学习了简单动态字符串,链表,字典,跳跃表,整数集合,压缩列表的数据结构,但是redis并没有使用整个数据结构直接实现键值对数据库,而是基于这些数据结构实现了对象系统,包含:字符串对象
,列表对象
,哈希对象
,集合对象
,有序集合对象
,这样做的好处是,可以针对不同的使用场景使用不同的数据结构,优化效率。
redis还实现引用计数器的内存回收机制,并且会让多个数据库键共享一个对来节约内存。
redis中的对象还带有访问时记录信息,在服务器其余maxmemory功能的时候,根据此信息会删除长时间没有被访问的对象
1.对象的结构
类型
redis数据库中,键固定式字符串对象,但是键可能是字符串,列表,哈希,集合,有序集合对象等。type字段就记录了到底是什么对象(redis客户端使用
Type 键名
将返回对象类型)编码
encoding字段记录了,底层实现使用了什么编码,每种类型的对象至少使用了两种不同类型的编码。
使用
object encoding 键名
可以获取对象的编码使用编码,可以让redis在不同的情况下,使用不同的底层数据结构,优化效率
比如在列表元素比较少的时候,redis使用压缩列表,也不是使用链表,就是因为压缩列表相比链表,少了前继,后继指针,使用连续的内存存储,压缩列表更加节约内存。随着元素越来越多,redis将转化使用双端链表进行保存
底层实现
redis使用一个指针,指向底层实现的数据结构
2.字符串对象String
2.1字符串对象的结构
字符串对象的编码可以使用int
,raw
,embstr
当字符串对象保存一个字符串值,并且长度大于39字节的时候,字符串对象将使用简单动态字符串来保存,并且指定编码为raw
当保存的内容是一个字符串值,但是字符串长度小于等于39字节的时候,redis使用
embstr
来保存使用SDS的raw编码,会使用两次内存分配函数,分别创建redisObject,和SDS,但是embStr编码则只需要一次内存分配获取一块连续的空间,一次存储redisObject和字符串内容
当字符串对象保存的是一个整数值,并且整数值可以使用long来表示,这是redis会使用int类型编码
2.2字符串对象命令
set
redis根据情况使用不同的编码保存字符串对象
get
返回值
append
在尾部追加,对于int编码或者embstr编码会将对象编码转化为raw,然后进行拼接
incrbyFloat
redis会尝试将字符串转化为long double类型的数字,然后进行加法运算
incrby
只有int编码可以进行此操作,进行整数加法运算
decrby
只有int编码可以进行此操作,进行整数减法运算
strlen
返回字符串长度
setrange
设置特定索引上的值,int 和 embstr编码都会先转换为raw然后进行操作
getrange
返回特定索引下的值
3.列表对象list
3.1列表对象的结构
列表对象的编码可以是ziplist,或者linkedlist
- 当列表中的字符串元素都小于64字节的时候,且数量小于521的时候使用ziplist进行保存
当列表中的字符串元素存在大于64字节的元素时候,或者数量大于等于521的时候使用linkedlist进行保存
3.2列表命令
lpush
将新元素压入列表头部
rpush
将新元素压入列表尾部
lpop
返回表头元素,并删除表头元素
rpop
返回表尾元素,并删除表尾元素
LIndex
定位列表指定节点,并返回节点保存的元素
LLen
返回列表长度
LInsert
插入新节点到指定位置
LREM
删除给定元素的节点
LTRIM
删除不在索引范围内的节点
LSET
设置指定索引位置的值
4.哈希对象hash
4.1哈希对象的结构
哈希对象的编码可以是ziplist,也可以是hashtable
ziplist编码底层使用压缩链表,当新元素加入的时候,现在压缩链表中存储key然后存储value
当键值字符串长度都小于64字节,且数量小于512的时候,使用此种编码
hashtable编码底层使用字典,每一个键都是字符串对象,每一个值也是字符串对象
当键值字符串长度存在大于等于64字节的,或者数量大于512的时候,使用此种编码
4.2哈希对象的命令
hset
设置哈希对象 key和对应的值
hget
获取哈希对象key对应的值
hexists
判断哈希对象是否存在key
hdel
删除哈希对象 key和对应的值
hlen
返回哈希对象具备的key数量
hgetall
返回哈希对象索引的键和队友的值
5.集合对象set
5.1集合对象的结构
集合对象编码可以是intset,或者hashtable
intset编码底层使用整数集合
当集合保存的全是整数,并且数量不超过512个的时候使用此种编码
hashtable底层使用字典,但是value全为null
当集合保存的不全是整数,或者数量超过512个的时候使用此种编码
5.2集合对象的命令
SCARD
返回集合元素的数量
SISMEMBER
判断元素是否存在于集合
SMENBERS
返回所有集合元素
SRANDMEMBER
从集合中随机返回一个
SPOP
随机删除一个元素,并返回
SREM
删除给定元素
6.有序集合对象ZSET
6.1有序集合对象的结构
有序集合的编码有ziplist或者skiplist
ziplist底层使用压缩列表,每一个集合元素使用两个紧挨在一起的压缩列表节点表示,一个保存集合成员,一个保存分值
当有序集合元素少于128个,且元素长度都小于64字节的时候使用此种编码
skiplist编码,使用zset结构作为底层实现,一个zset包含一个字典,和一个跳跃表
当有序集合元素不少于128个,或者元素长度存在大于等于64字节的时候使用此种编码
跳跃表按照分值从小到大保存了所有集合元素,字典为有序集合创建了成员到分值的映射
二者的结合保证,范围查找和获取成员的分值都有较高的速度,范围型操作比如ZRANK,ZRANGE基于跳表进行,获取成员分值这种操作基于字典进行
二者保存的对象是共享的,不会使用两份空间进行保存
6.2有序集合对象的命令
ZADD
向有序集合存入元素和对应的分值
ZCARD
返回有序集合元素数量
ZCOUNT
返回有序集合指定分值范围元素的个数
ZRANGE
返回指定分值范围内的元素
ZRANK
返回元素和对应的排名
ZREM
删除元素
ZSCORE
返回给定元素的分值
Redis数据结构与对象的更多相关文章
- Redis数据结构和对象三
1.Redis 对象系统 Redis用到的所有主要数据结构,简单动态字符串(SDS).双端链表.字典.压缩列表.整数集合.跳跃表. Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些 ...
- redis数据结构和对象二
跳跃表(skiplist) 跳跃表是一种有序数据结构.跳跃表支持平均O(logN),最坏O(N)复杂度的节点查找,大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树简单,所有不 ...
- redis数据结构和对象一
1. SDS:简单动态字符串(simple dynamic string) Redis没有直接使用C语言的字符串,而是自己构建了一种名为简单动态字符串类型,并将SDS用作Redis的默认字符串. SD ...
- Redis学习笔记一:数据结构与对象
1. String(SDS) Redis使用自定义的一种字符串结构SDS来作为字符串的表示. 127.0.0.1:6379> set name liushijie OK 在如上操作中,name( ...
- Redis 基础数据结构与对象
Redis用到的底层数据结构有:简单动态字符串.双端链表.字典.压缩列表.整数集合.跳跃表等,Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包 ...
- redis 系列9 对象类型(字符串,哈希,列表,集合,有序集合)与数据结构关系
一.概述 在前面章节中,主要了解了 Redis用到的主要数据结构,包括:简单动态字符串.链表(双端链表).字典.跳跃表. 整数集合.压缩列表(后面再了解).Redis没有直接使用这些数据结构来实现键值 ...
- Redis 的底层数据结构(对象)
目前为止,我们介绍了 redis 中非常典型的五种数据结构,从 SDS 到 压缩列表,这都是 redis 最底层.最常用的数据结构,相信你也掌握的不错. 但 redis 实际存储键值对的时候,是基于对 ...
- [redis读书笔记] 第一部分 数据结构与对象 对象类型
- 从前面redis的基本数据结构来看,可以看出,redis都是在基本结构(string)的基础上,封装了一层统计的结构(SDS),这样让对基本结构的访问能够更快更准确,提高可控制度. - redis ...
- Redis | 第一部分:数据结构与对象 上篇《Redis设计与实现》
目录 前言 1. 简单动态字符串 1.1 SDS的定义 1.2 空间预分配与惰性空间释放 1.3 SDS的API 2. 链表 2.1 链表与节点的定义 2.2 链表的API 3. 字典 3.1 哈希表 ...
- Redis | 第一部分:数据结构与对象 下篇《Redis设计与实现》
目录 前言 1. Redis对象概述 1.1 对象的定义 2. 字符串对象 3. 列表对象 3.1 quicklist 快速链表 4. 哈希对象 5. 集合对象 6. 有序集合对象 7. Redis对 ...
随机推荐
- 《MySQL自传》
撰写本文查阅了大量参考资料,也得到很多朋友的指点帮助,特别感谢: Jimmy Yang--阿里云数据库研究员,原Oracle InnoDB Architect. 彭立勋--华为云数据库总工程师,MyS ...
- cmd常用命令介绍
一.cdm命令介绍:CMD命令是一种命令提示符,CMD是command的缩写,即命令提示符(CMD),位于C:\Windows\System32的目录下,是在OS/2,Win为基础的操作系统(包括Wi ...
- 2022.9.10-2022.9.12 Java第一次课总结
本节课中的问题总结如下: 1.Java的基本运行单位是类还是方法? 答:Java的基本运行单位是类. 2.类由什么组成? 答:类由变量/方法/属性/事件等部分组成,其中方法就是我们所熟悉的函数,属性即 ...
- linux 安装/卸载go环境
linux 安装/卸载go环境(基于centos8) 安装 下载go的安装包 Golang官网下载地址:https://golang.org/dl/ 将安装包解压放到到usr/local中,并解压 c ...
- Node.js躬行记(24)——低代码
低代码开发平台(LCDP)是无需编码(0代码)或通过少量代码就可以快速生成应用程序的开发平台.让具有不同经验水平的开发人员可以通过图形化的用户界面,通过拖拽组件和模型驱动的逻辑来创建网页和移动应用程序 ...
- 5.RabbitMQ系列之headers交换器
headers exchange是根据消息header值而不是routing key将消息路由到队列的交换器. 生产者在消息头中以键值对的形式添加一些值,并将其发送到headers exchange, ...
- python读取文本数据某一列
import codecs f = codecs.open('test1 - 副本.txt', mode='r', encoding='utf-8') # 打开txt文件,以'utf-8'编码读取 l ...
- 基于FPGA的AES加解密IP
Programmable AES Encryption/ Decryption IP 可编程AES加解密IP 可编程AES加解密IP提供了加解密算法功能,兼容美国国家标准与技术研究院(NIST)发布的 ...
- 论文笔记 - Active Learning by Acquiring Contrastive Examples
Motivation 最常用来在 Active Learning 中作为样本检索的两个指标分别是: 基于不确定性(给模型上难度): 基于多样性(扩大模型的推理空间). 指标一可能会导致总是选到不提供有 ...
- Java安全之CC6
前言 之前三篇详细分析了CommonsCollections1利用链,两种方法,LazyMap以及TransformedMap,但是在Javaa 8u71以后,这个利⽤链不能再利⽤了,主要原因是 su ...