Redis设计与实现
前言
redis版本说明
数据结构与对象
简单动态字符串
SDS
可以修改
区别于C语言字符串
三个属性
free:值为0表示未分配使用空间
len :属性值为5表示SDS保存了五字节长的字符床
buf:属性是char类型的数组
SDS与C字符串的区别
SDS
常数复杂度获取SDS长度
len 属性 确保获取长度不成为性能瓶颈
C语言
需要遍历字符串长度
杜绝缓冲区溢出
扩容之前判断len是否满足扩容
减少修改字符串带来的内存重新分配次数
空间预分配
预先分配free 空间
SDS的长度大于等于1M,free大小将分配1M
SDS长度小于1M,free大小预分配len属性大小同样长度
惰性空间释放
删除字符串之后,将删除空间分配到free属性中
二进制安全
C语言使用空串作为结尾
SDS可以保存二进制文件,不会丢失包含空串数据
兼容部分C字符串
链表
特点
特点
高效重排能力
顺序节点访问
redis 自己构建了链表实现(C语言没有链表)
链表和链表节点的实现
listNode (双端链表)实现
特点
双端
无环
带有表头指针,表尾指针
持有长度计数器
多态(可以保存多种数据类型)
字典
特点
C语言未内置字典数据结构
redis自己实现字典
字典是hash键的底层实现之一
字典的实现
hash表
table
size
used
sizemask
决定新增数据应该放到数组哪个索引上面
hash表节点
dictEntry
union{}
next
hash算法
计算hash值
根据hash值计算index
解决键冲突
hash表出现同一个数据在同一个索引上
新加入数据总在表头位置
rehash
扩展和收缩通过rehash(重新散列实现)
创建新的表
根据旧表hash计算索引和新表的值
复制旧表数据到新表
释放旧表
渐进式rehash
rehash动作不是一次完成的6
数据量巨大的时候
分多次
渐进式rehash
分而治之的思想,庞大的计算量平均到每一次hash计算
具体步骤
分配新表(字典同时持有旧表和新表)
rehashindex变量==0 rehash正式开始
rehash期间,除去对数据增删查操作外,旧表数据同时rehash到新表上
旧表不再新增数据,全部新增到新表上
跳跃表
特点
有序的数据结构
平均时间复杂度O(logn),最坏O(N)
效率媲美平衡树,实现更加简单
跳跃表使用的地方:有序集合&集群节点内部数据结构使用跳跃表
跳跃表最高32层
跳跃表数据结构
zskipList
zskipListNode
head
tail
len
同一个跳跃表多个节点包含相同的分值,每个节点的成员对象必须是唯一的
跳跃表中的节点按照分值大小进行排序,分值大小相同的时候,按照成员大小进行排序
整数集合
特点
集合键的底层实现之一
升级过程不可逆
压缩列表
特点
列表键和hash键的底层实现之一
redis为了节约内存开发出压缩列表
包含任意多个节点
对象
对象类型与编码
类型
字符串对象
列表对象
zset 同时使用跳跃表和字典实现
跳跃表范围查询快,根据key寻找value慢
字典表范围查找慢,需要额外空间进行数据排序 ,查询慢
hash对象
集合对象
有序集合对象
内存回收
C语言不具有自动内存回收功能,redis在自己内存系统构建一套回收机制
对象引用计数信息
创建对象时 引用计数初始化为1
新程序使用时,引用计数值增加1
不被一个程序使用时间,引用计数值-1
引用计数归0时间,对象占用内存会被释放
对象共享
共享步骤
将键指针指向已经存在的对象
将共享的对象的引用计数+1
对象的空转时长
当前时间-lru时长
重点
redis数据库中的每个键值对的key和value 都是对象
对象类型
字符串
列表
hash
集合
有序集合
每种集合最少包含两种编码方式,不同的编码方式可以在不同的场景优化对象的使用效率
具体编码方式和区别
todo
redis对应系统带有引用计数实现的内存回收机制,对象不再使用 ,自动回收
redis会共享0-9999的字符串对象
对象会记录最后一次的访问时间,这个时间用于计算对象的空转时间
单机数据库实现
数据库
服务器中的数据库
服务器dbnum决定6Redis服务器初始化数据库数量,默认数据库16个
切换数据库
客户端无法显示目标数据库号码,执行flushDb等命令的时候,最好先执行一下select 显式切换到指定数据库
数据库键空间
添加新键
删除键
更新键
对键取值
其他键空间操作
flushdb
dbSize
exists
keys
读写键空间时的维护操作
更新命中次数
更新LRU时间
读取键数据时发现键数据已经过期,优先删除过期数据
使用watch命令监视某个键,服务器会对被监视的数据标记为dirty ,从而让事物程序注意到某个键已经修改过
服务器修改数据之后,会对相应的数据标记dirty,该键计数器值增加1
服务器开启通知,对数据修改之后,服务器按照配置发送相应数据到数据库
设置键生存时间或者过期时间
tips
setnx: 只能用于字符串
设置过期时间
保存过期时间
移除过期时间
PERSIST:移除过期时间
计算并返回剩余生存时间
TTL:秒级别返回剩余生存时间
PTTL:毫秒级别返回过期时间
过期键的判定
过期字典查询
TTL命令
过期删除策略
定时删除
惰性删除
定期删除
redis的过期删除策略
惰性删除的实现
定期删除策略的实现
AOF|RDB和复制功能对过期键的处理
生成RDB文件
载入RDB文件
AOF写入
复制
数据库通知
redis 2.8增加通知功能
根据通知显示,三条命令对message进行操作
set
expire
del
发送通知
发送通知的实现
重点回顾
redis服务器的所有数据库都保存在redisServer..db数组中,数据库数量保存在redisserver.dbnum中
RDB持久化
特点
redis是键值对数据库服务器
RDB持久化可以手动执行,也可以根据服务器配置定期执行
RDB文件持久化的是经过压缩的二进制文件
具体持久化命令
SAVE()
BGSAVE()
RDB文件的创建与载入
两条命令
SAVE()
阻塞redis进程,服务器不能处理任何命令请求
BGSAVE()
创建子进程,子进程创建RDB文件,服务器进程继续处理命令请求
RDB文件只有在启动服务器的时候自动执行,没有专门执行RD9B文件的命令
AOF文件更新频率更高
开启AOF文件持久化,服务器优先使用AOF模式还原数据
save命令执行时服务器的状态
阻塞
BGSAVE命令执行时服务器状态
阻塞
RDB文件载入的时候服务器状态
阻塞
自动保存时间间隔
设置保存条件
检查保存条件是否满足
RDB文件结构
分支主题
AOF持久化
特点
AOF:保存的命令
RDB:保存的是具体键值对
AOF持久化的实现
命令追加
AOF文件的写入和同步
AOF文件的载入和还原
AOF重写
AOF重写功能实现
旧AOF文件包含所有键值对命令
重写完成的AOF文件吗,包含能恢复当前数据库状态的数据命令
AOF后台重写
创建子进程进行重写AOF文件操作
避免锁的情况下保证数据安全
设置AOF文件缓冲区
子进程重写AOF文件期间服务器主进程
1、执行客户端命令
2、执行后的命令追加到旧AOF文件
3、执行后的命令追加到AOF重写缓冲区
子进程完成工作后,发送信号到父进程,父进程调用函数
1、重写内容替换到缓冲区AOF文件
2、新AOF文件和旧AOF文件的替换
只有调用函数的部分线程会阻塞
事件
特点
redis 是事件驱动程序
文件事件
套接字进行链接
文件事件是对套接字操作的抽象
服务器通过监听并处理这些事件
实现机制
网络事件处理器:文件处理器
使用IO多路复用IO,同时监听多个套接字
被监听的套接字执行链接
accept
read
write
close
高性能网络通信模型
文件事件处理器的构成
构成
套接字:套接字看作不同主机间的进程进行双间通信的端点,它构成了单个主机内及整个网络间的编程界面
IO多路复用
监听套接字
传送套接字
文件事件分派器
产生事件的套接字放到一个队列里面,队列有序,同步的向事件分派器发送套接字
事件处理器
IO多路复用IO的实现
包装常用的select、epoll、evport和kqueue等IO复用函数库实现
redis为每个IO多路复用IDO复用函数都实现了相同的API,所以IO多路复用IO的程序的底层是可以互换的
编译时总会选择性能最高的IO复用函数作为复用程序的实现
事件类型
可读操作,调用AE_readable
可写操作,调用AE_writeable
同时发生,写操作在读操作之后
API
文件事件处理器
链接应答处理器
命令请求处理器
命令回复处理器
时间事件
redis服务器在给定的时间点执行
特点
定时事件
周期性事件
时间事件属性
id:全局唯一
when:
timeProc:事件事件处理器:调用响应处理器处理事件
实现
所有的时间事件都放在一个无序链表中,每当事件事件执行的时候,遍历整个链表处理事件
时间事件应用实例:“serverCorn函数
更新服务器统计信息
清理数据库过期键值对
关闭清理过期失效客户端
尝试进行AOF|RDB持久化操作
主服务器:同步从库文件
集群模式:定期同步和链接测试
事件的调度与执行
客户端
特点
分支主题
服务器
命令请求的执行过程
发送命令请求
读取命令请求
命令执行器:
查找命令实现
执行预备操作
调用命令实现函数
执行后续工作
命令回复发送客户端
客户端接收打印命令回复
serveCorn函数
更新服务器时间缓存
更新LRU时钟
多机数据库实现
复制
旧版复制功能的实现
命令传播
同步数据库修改状态同步
分支主题
同步
数据库状态的更新到主服务器状态
1、发送sync命令
2、执行bgsave命令,生成RDB文件,使用缓冲区执行所有命令
3、同步到从服务器
新版复制功能实现
分支主题
sentinel
集群
独立功能实现
发布与订阅
事物
lua脚本
排序
二进制位数组
慢日志查询
监视器
resp协议
RESP 协议有如下几个特点:
实现简单;
快速解析;
可阅读;
RESP 是基于 TCP 协议实现的
可以使用socket链接