Redis数据库学习第二篇

本文深入探讨Redis数据库中使用的各种高效数据结构,包括简单动态字符串、链表、字典、跳跃表、整数集合和压缩列表,以及这些数据结构如何支持Redis的键值对存储、查询和更新操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

此篇介绍在Redis数据库中的数据结构与对象

一,简单动态字符串

Redis自己构建了一种名为简单动态字符串(SDS)的抽象类型,并将SDS用作Redis的默认字符串表示。

所以,SDS和C语言中的字符串类型有什么不一样呢?

常数复杂度获取字符串长度
程序计算一个C字符串长度的过程,时间复杂度是O(N),因为需要指针遍历整个数组才能得到长度,而SDS只需要访问len属性就可以了。


杜绝缓冲区溢出
在C中实行字符串拼接操作时(比如strcat函数)很容易由于未分配足够空间而造成缓冲区溢出的现象
SDS需要进行修改时,API会先检查SDS的空间是否满足需求,如果不满足会自动将SDS扩容至修改所需的大小


减少修改字符串时带来的内存重分配次数
由于SDS的API比较多,内存会执行预分配策略和惰性空间释放。


二进制安全
SDS并不像C语言那样,使用'\0'作为判定一个字符串的结尾,而是使用了独立的len,这样可以保证即使存储的数据中有'\0'这样的字符,它也是可以支持读取的.(之所以叫二进制安全是因为二进制安全只关心二进制化的字符串,不关心具体格式.只会严格的按照二进制的数据存取。不会妄图已某种特殊格式解析数据。)


兼容部分C字符串函数

可以使用一部分<String.h>中的函数。

总结:

二,链表

链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表地长度。链表被广泛用于实现Redis地各种功能,比如列表键,发布与订阅,慢查询,监视器等。
每个链表节点由一个listNode结构来表示,每个节点都有一个指向前置节点和后置节点地指针,所以Redis地链表实现是双端链表。


每个链表使用一个list结构来表示,这个结构带有表头结点指针,表尾节点指针以及链表长度等信息
Redis链表可以用于保存各种不同类型的值

三,字典

字典是一种用于保存键值对的抽象数据结构,字典中每个键都是独一无二的,程序可以在字典中根据键查找或更新与之相关的值。

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

字典结构dict中的属性中,type和privdata属性是针对不同类型的键值对,为创建多态字典而设置的。type属性是一个指向dictType结构的指针,每个dictType结构保存了一组用于操作特定类型键值对的函数。privdata属性保存了需要传给那些类型特定函数的可选参数。ht属性是一个包含两个项的数组,数组每个项都是一个哈希表,备用哈希表在哈希表rehash时使用rehashidx。

Redis的哈希表结构dictht中,table是一个数组,每一个元素都是一个指向dictEntry结构的指针,每个dictEntry结构保存着一个键值对。size属性记录了哈希表的大小,used属性记录哈希表目前已有节点的数量。

哈希表节点用dictEntry结构表示,每个dictEntry结构都保存着一个键值对,key属性保存着键值对中的键,而v属性保存着键值对中的值,next属性是指向另一个哈希表节点的指针,连接多个哈希值相同的键值对以解决键冲突的问题。

当字典被用作底层实现时,redis使用MurmurHash2算法来计算键的哈希值。

在渐进式rehash过程中,字典会同时使用ht[0]和ht[1]两个哈希表,所以在渐进式rehash进行期间,字典的删除,查找,更新等操作会在两个哈希表上进行。如果要在字典中查找一个键,程序会在ht[0]里面进行查找,如果没找到,就会在ht[1]里继续查找。在rehash期间,新添加到字典的键值对会保存在ht[1]里面,ht[0]里面的键值对数量会只减不增,并最终成为空表。

 

四,跳跃表

跳跃表是一种有序的数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。
在大部分情况下,跳跃表的效率可以对比于平衡树,而且实现比平衡树更简单。Redis只在两个地方用到了跳跃表,一是实现有序集合键,另一个是在集群节点中作为内部数据结构。

Redis跳跃表zskiplist属性:header 指向跳跃表的表头节点;tail 指向跳跃表的表尾节点  level记录目前跳跃表内,层数最大的那个节点层数;length跳跃表长度。
层: 跳跃表中每个节点的层高都是1至32的随机数,用Ln表示。
前进指针:指向前面节点的指针
跨度:记录两个节点间的距离
后退指针:用BW表示
分值和成员:在同一个跳跃表中,多个节点可以包含相同的分值,但每个节点的成员对象必须是唯一的。跳跃表中的节点按分值大小进行排序,当分值相同时,节点按照成员对象大小进行排序。

五,整数集合

整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,并且集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。

整数集合的底层实现为数组,这个数组以有序,无重复的方式保存集合元素,在有需要时,程序会根据新添加元素的类型,改变这个数组的类型。

contents数组是整数集合的底层实现:整数集合的每个元素都是contents数组的一个数组项,各个项在数组中按值的大小有序排列,并且数组中不包含任何重复项。length属性记录contents数组的长度。
每当我们要将一个新元素添加到整数集合里面,并且新元素的类型比整数集合现有所有元素类型都要长时,整数集合需要先进行升级,然后才能将新元素添加到整数集合里面。
升级操作为整数集合带来了操作上的灵活性,并且尽可能节约了内存。
整数集合只支持升级操作,不支持降级操作。

六,压缩列表

压缩列表是列表键和哈希表的底层实现之一。当一个列表键只包含少量列表项,并且每个列表都是小整数值或者长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。


Redis在特殊情况下产生连续多次空间扩展操作称之为“连锁更新”,压缩列表里恰好有多个连续的,长度介于长度属性临界值之间的节点,连锁更新才有可能被引发,在实际上,这种情况并不多见。
其次,即使出现连锁更新,但只要被更新的节点数量不多,就不会对性能造成影响

七,对象

Redis没有直接使用数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,包含字符串对象,列表对象,哈希对象,集合对象和有序集合对象这五种类型的对象。通过这五种不同类型的对象,Redis可以在执行命令之前,根据对象类型诊断一个对象是否可以执行给定的命令,并提高不同场景下的使用效率。

Redis使用对象来表示数据库中的键和值,每次当我们在Redis数据库中新创建一个键值对时,我们会创建一个键对象和一个值对象。服务器在执行某些命令之前,会检查给定键的类型能否执行指定的命令,而检查一个键的类型就是检查键的值对象的类型

除此之外,Redis对象系统还实现了基于计数技术的内存回收机制和对象共享机制。
Redis的对象系统带有引用计数实现的内存回收机制,当一个对象不再使用的时候,该对象所占用的内存就会被自动释放。对象会记录自己的最后一次访问时间,这个时间可以用于计算对象的空转时间。

 

字符串对象的使用

redis 127.0.0.1:6379> SET name "redis"
OK
redis 127.0.0.1:6379> GET name
"redis"

在以上实例中我们使用了 Redis 的 SET 和 GET 命令。键为 name,对应的值为 redis。

哈希对象的使用

redis> HMSET myhash field1 "Hello" field2 "World"
"OK"
redis> HGET myhash field1
"Hello"
redis> HGET myhash field2
"World"

 实例中我们使用了 Redis HMSET, HGET 命令,HMSET 设置了两个 field=>value 对, HGET 获取对应 field 对应的 value。

列表对象的使用

redis 127.0.0.1:6379> lpush lishinho redis
(integer) 1
redis 127.0.0.1:6379> lpush lishinho mongodb
(integer) 2
redis 127.0.0.1:6379> lpush lishinho rabitmq
(integer) 3
redis 127.0.0.1:6379> lrange lishinho 0 10
1) "rabitmq"
2) "mongodb"
3) "redis"
redis 127.0.0.1:6379>

lpush命令把新元素压入压缩列表的表头,lrange输出在列表标号值范围的元素。

集合对象的使用

redis 127.0.0.1:6379> sadd lishinho redis
(integer) 1
redis 127.0.0.1:6379> sadd lishinho mongodb
(integer) 1
redis 127.0.0.1:6379> sadd lishinho rabitmq
(integer) 1
redis 127.0.0.1:6379> sadd lishinho rabitmq
(integer) 0
redis 127.0.0.1:6379> smembers lishinho

1) "redis"
2) "rabitmq"
3) "mongodb"

sadd命令:添加一个 string 元素到 key 对应的 set 集合中,成功返回1,如果元素已经在集合中返回 0,如果 key 对应的 set 不存在则返回错误。

有序集合对象

redis 127.0.0.1:6379> zadd lishinho 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd lishinho 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd lishinho 0 rabitmq
(integer) 1
redis 127.0.0.1:6379> zadd lishinho 0 rabitmq
(integer) 0
redis 127.0.0.1:6379> > ZRANGEBYSCORE lishinho 0 1000
1) "mongodb"
2) "rabitmq"
3) "redis"

zadd命令添加元素到集合,元素在集合中存在则更新对应score。

参考文献:redis设计与实现第二版 黄建宏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值