Redis总结

第二章 简单动态字符串

SDS:Simple Dynamic String
结构以及举例
在这里插入图片描述
比起C字符串
1)常数复杂度获取字符串长度,因为有len属性
2)杜绝缓冲区溢出:当SDS API需要对SDS进行修改时,API会先检查SDS的空间是否满足修改所需的要求,如果不满足的话,API会自动将SDS的空间扩展至执行修改所需的大小,然后执行操作。
3)减少修改字符串时带来的内存重分配次数
1.空间预分配,如果对SDS进行修改之后,SDS的长度(即len的值)小于1MB,free
= len,若大于等于1MB,free = 1MB
2.惰性空间释放,多出来的空间不会立马释放,SDS也提供了相应的API,可以在有
需要时,真正地释放SDS的未使用空间
4)二进制安全,所有SDS API都会以处理二进制的方式处理SDS存放在buf数组里的数据,Redis不是用buf来保存字符,而是用它来保存一系列二进制数据。
5)兼容部分C字符串函数,因为它以空字符结尾。

第三章:链表

结构以及举例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
特点
1)双端:链表节点都带有prev和next指针
2)无环:表头节点的prev指向null,表尾节点的next指向null
3)带表头指针和表尾指针:获取表头和表尾的复杂度为O(1)
4)带链表长度计数器:用len属性计数,获取节点数量的复杂度为O(1)

第四章 字典

结构以及举例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
采用链地址法解决冲突,采用头插法增加新节点
rehash
1)为字典的ht[1]哈希表分配空间,如果是扩展工作,ht[1]为第一个大于等于ht[0].used*2
的2n(n在右上角),如果是收缩操作,ht[1]的大小为第一个大于等于ht[0].used的
2n(n在右上角)
2)将保存在ht[0]中的所有键值对rehash到ht[1]上面,rehash指的是重新计算键的哈希值
和索引值,然后将键值对放置到ht[1]哈希表的指定位置上。
3)当ht[0]包含的所有键值对都迁移到了ht[1]之后(ht[0]变为空表),释放ht[0],将ht[1]
设置为ht[0],并在ht[1]新创建一个空白哈希表,为下一次rehash做准备。
哈希表的扩展与收缩
当以下条件中的任意一个被满足时,程序会自动开始对哈希表执行扩展操作:
1)服务器目前没有正在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于1
2)服务器目前正在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于5
当哈希表的负载因子小于0.1时,程序自动开始对哈希表进行收缩操作
渐进式rehash
步骤:
1)为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两个哈希表
2)在字典中维持一个索引计数器变量rehashidx,并将它的值设置为0,表示rehash工作正式开始
3)在rehash进行期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],当rehash工作完成后,rehashidx加1
4)随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有键值对都会被rehash到ht[1],这时将rehashidx设为-1,表示rehash操作已完成。
渐进式rehash执行期间的哈希表操作
字典的删除,查找,更新会在两个哈希表上进行。例如:查找一个健,先在ht[0]上查找,若没有找到,再在ht[1]上查找,诸如此类。添加操作一律会被保存到ht[1]里面,保证ht[0]中键值对数量只减不增,最终会成为空表。

第五章 跳跃表

跳跃表:跳跃表是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。跳跃表支持平均O(logn),最坏O(n)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。在大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树要来得更为简单,所以有不少程序都使用跳跃表来代替平衡树,Redis使用跳跃表作为有序集合键的底层实现之一。
结构以及举例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
层的跨度(level[i].span属性)用于记录两个节点之间的距离,初看上去,很容易以为跨度和遍历操作有关,但实际上并不是这样,遍历操作只使用前进指针就可以完成了,跨度实际上是用来计算排位(rank)的:在查找某个节点的过程中,将沿途访问过的所有层的跨度累计起来,得到的结果就是目标节点在跳跃表中的排位。
如果分值相同的话,按照成员对象在字典序中的大小来进行排序(由小到大),分值可以相同,成员对象必须唯一。

第六章 整数集合

整数集合是集合键的底层实现之一,当一个集合只包含整数元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。
结构以及举例
在这里插入图片描述
在这里插入图片描述
整数集合的每个元素都是contents数组的一个数组项(item),各个项在数组中按值的大小从小到大有序地排列,并且数组中不包含任何重复项。
升级支持,降级不支持
因为每次向整数集合中添加新元素都会引起升级,而每次升级都需要对底层数组中已有的元素进行类型转换,所以向整数集合中添加新元素的时间复杂度为O(n)。

第七章 压缩列表

压缩列表(ziplist)是列表键和哈希键和有序集合键的底层实现之一,当一个列表键只包含少量列表项,并且每个列表项要么就是最小整数值,要么就是长度比较短的字符串,那么Redis会使用压缩列表来实现列表键。哈希键,有序集合键同理。

压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成组成的顺序型(sequential)数据结构。一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。
结构以及举例:
压缩列表举例
在这里插入图片描述
压缩列表各个组成部分的详细说明

属性类型长度用途
zlbytesuint32_t4字节记录整个压缩列表占用的内存字节数
zltailuint32_t4字节记录压缩列表表尾节点距离压缩列表的起始地址有多少字节
zllenuint16_t2字节记录了压缩列表包含的节点数量,当小于65535时,是节点数量,等于65535时,需遍历才能得到节点数量
entryX列表节点不定压缩列表包含的各个节点,节点的长度由节点保存的内容决定
zlenduint8_t1字节特殊值0XFF(十进制255),用于标记压缩列表的末端

每个压缩列表节点都由previous_entry_length,encoding,content三个部分组成
previous_entry_length记录了前一节点的长度(以字节为单位),可以实现从尾到头遍历(1字节或者5字节)
encoding记录了节点的content属性所保存数据的类型以及长度
content:节点的content属性负责保存节点的值,节点值可以是一个字节数组或者整数,值的类型和长度由节点的encoding属性决定
连锁更新:Redis将在特殊情况下产生的连续多次空间扩展操作称之为“连锁更新”,很少出现。

第八章 对象

不同类型和编码的对象

类型编码对象
REDIS_STRINGREDIS_ENCODING_INT使用整数值实现的字符串对象
REDIS_STRINGREDIS_ENCODING_EMBSTR使用embstr编码的简单动态字符串的字符串对象
REDIS_STRINGREDIS_ENCODING_SDS使用简单动态字符串实现的字符串对象
REDIS_LISTREDIS_ENCODING_ZIPLIST使用压缩列表实现的列表对象
REDIS_LISTREDIS_ENCODING_LINKEDLIST使用双端列表实现的列表对象
REDIS_HASHREDIS_ENCODING_ZIPLIST使用压缩列表实现的哈希对象
REDIS_HASHREDIS_ENCODING_HT使用字典实现的哈希对象
REDIS_SETREDIS_ENCODNG_INTSET使用整数集合实现的集合对象
REDIS_SETREDIS_ENCODING_HT使用字典实现的集合对象
REDIS_ZSETREDIS_ENCODING_ZIPLIST使用压缩列表实现的有序集合对象
REDIS_ZSETREDIS_ENCODING_SKIPLIST使用跳跃表和字典实现的有序集合对象

字符串编码的转换
int->raw,对象不再是整数值
embstr->raw,被改变
列表编码的转换
1)长度小于64字节
2)数量小于512
同时满足上述两个条件使用ziplist,否则使用linkedlist
哈希编码的转换
1)键和值的长度都小于64字节
2)数量小于512
同时满足上述两个条件使用ziplist,否则使用hashtable
集合编码的转换
1)元素是整数值
2)数量小于512
同时满足上述两个条件使用intset,否则使用hashtable
有序集合编码的转换
1)长度小于64字节
2)数量小于128
同时满足上述两个条件使用ziplist,否则使用skiplist
有序集合的结构
在这里插入图片描述
可以看到有序集合并不只是用跳跃表来实现的,里面包含了一个跳跃表和一个字典,跳跃表用于实现ZRANK,ZRANGE等函数,字典用于实现ZSCORE等函数。跳跃表和字典通过指针来共享相同元素的成员和分值,不会浪费额外的内存。
refcount用于内存回收,对象共享(只对包含整数的字符串对象共享)
lru:最近访问时间,可用于内存回收(引用计数算法)

第九章 数据库

读写键空间时的维护操作
1)更新键的lru时间
2)若是修改操作,dirty+1
3)如果发现键过期,则删除
activeExpireCycle函数用来删除过期键,每当ServerCron函数执行时,activeExpireCycle函数就会被调用
删除过期键的过程:在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除过期键
activeExpireCycle函数的具体过程:
全局变量current_db会记录检查进度,在下一次activeExpireCycle函数调用时,接着上一次的进度进行处理。随着函数的不断执行,服务器中的所有数据库都会被检查一遍,此时,将current_db重置为0,开始新一轮的检查。
AOF RDB和复制功能对过期键的处理
生成RDB文件:数据库中包含过期键不会对生成新的RDB文件造成影响,过期键会被忽略
载入RDB文件:如果是主服务器,过期键会被忽略,如果是从服务器,不会忽略过期键,同步之后从服务器中的过期键被清空,没有问题
AOF文件写入:过期键不会对AOF文件产生任何影响,当被删除之后,会向AOF文件中追加(append)一条del命令,来删除该键。
AOF重写:数据库中包含过期键不会对AOF造成影响,过期键会被忽略。
复制:当服务器运行在复制模式下时,从服务器的过期键删除动作由主服务器来控制。
第十章RDB持久化
RDB持久化可以将某个时间点上的数据库状态保存到一个RDB文件中,RDB文件是一个经过压缩的二进制文件,有两个Redis命令可以用于生成RDB文件,一个是SAVE,一个是BGSAVE,SAVE命令会阻塞Redis服务器进程,BGSAVE命令会派生出一个子进程,负责创建RDB文件,服务器进程(父进程)继续处理命令请求。
redisServer与RDB有关的结构:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
检查保存条件是否满足
Redis的服务器周期性操作函数ServerCron函数默认每隔100毫秒就会执行一次,程序会遍历并检查saveparams数组中保存的所有条件,只要有任意一个条件被满足,那么服务器就会执行BGSAVE命令。
第十一章 AOF持久化
命令追加:将写命令追加到aof_buf缓冲区
文件写入:保存到内存缓冲区
同步:写入到磁盘
AOF文件的载入与数据还原
创建伪客户端,读取并执行写明命令
AOF重写:读取当前的数据库状态,对每个键值对,记录一条命令。
使用子进程来执行AOF重写:此时,服务器会将执行后的写命令追加到AOF重写缓冲区,当重写完成后,向父进程发送信号,然后父进程将AOF重写缓冲区的内容写入新AOF文件,对新AOF文件改名,原子地覆盖现有的AOF文件。
写入和同步的时机,由flushAppendOnlyFile函数来确定

appendfsync的值flushAppendOnlyFile
always总是写入并同步
everysec(默认值)写入,每隔一秒执行一次
no写入,何时同步由操作系统决定

Redis常用命令

对象命令
type key
object encoding key
object refcount key
object idletime key
键命令
keys *
exists key
del key
expire key
pexpire key
expireat key
pexpireat key
ttl key
pttl key
persist key
与服务器有关的命令
save
bgsave
bgrewriteaof
字符串命令
set key value
get key
strlen key
列表命令
lpush key value(可以有多个)
rpush key value(可以有多个)
lpop key
rpop key
lrem key count value
如果count = 0,全部删除
如果count < 0,从尾到头删除|count|个value
如果count > 0,从头到尾删除count个value
lrange key fromIndex toIndex
llen key
哈希命令
hset key field value
hmset key field1 value1 field2 value2…
hdel key field(可以有多个)
hexists key field
hget key field
hmget key field1 field2…
hgetall key
hkeys key
hvals key
hlen key
集合命令
sadd key value(可以有多个)
srem key value (可以有多个)
sismember key value
smembers key
scard key
有序集合命令
zadd key score value(可以有多个)
zrem key value(可以有多个)
zremrangebyrank key index1 index2
zremrangebyscore key score1 score2
zscore key value
zrank key value
zrevrank key value
zrange key fromIndex toIndex
zrangebyscore key score1 score2
zrevrange key fromIndex toIndex
zrevrangebyscore key score1 score2
zcount key score1 score2
zcard key
如果有分数的话,有括号,不含边界,无括号,含边界
对象
typedef struct redisObject{
//类型
unsigned type:4
//编码
unsigned encoding:4
//指向底层实现数据结构的指针
void *ptr
//引用计数
int refcount
unsigned lru:22
}
服务器
struct redisServer{
//一个数值,保存着服务器中的所有数据库
redisDb * db
//服务器的数据库数量
int dbnum
//记录了保存条件的数组
struct saveparam *saveparams;
//修改计数器
long long dirty
//上一次执行保存的时间
time_t lastsave
//aof缓冲区
sds aof_buff
}
数据库
typedef struct redisDb{
//数据库键空间,保存着数据库中的所有键值对
dict *dict
//过期字典,保存着键的过期时间
dict *expires
}
Redis的服务器进程是一个事件循环
def eventLoop():
while true:
#处理文件事件,接收命令请求以及发送命令回复
#处理命令请求时可能会有新的内容被追加到aof_buf缓冲区中
processFileEvents()
#处理时间事件
processTimeEvents()
#考虑是否要将aof_buf文件中的内容写入和保存到aof文件里面
flushAppendOnlyFile()

时间事件负责执行像serverCron函数这样需要定时运行的函数,每隔100毫秒运行一次
RDB在serverCron函数中执行,activeExpireCycle函数在serverCron函数中执行,RDB用来进行RDB持久化,activeExpireCycle函数用来删除过期键。

参考

《Redis设计与实现》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值