《Redis设计与实现》之单机数据库实现

服务器

数据库

redis客户端都有自己的目标数据库,默认为0号数据库,使用select进行切换

struct redisServer{
       int dbnum; //服务的数据库数量,由数据库配置database决定,默认16
       redisDb *db; //保存服务器中所有数据库信息
       saveparam *saveparams; //保存条件的数组 saveparam={seconds,changes}
}
struct redisClient{
       redisDb *db; //记录客户端正在使用的数据库,指向redisServer.db数组
}

客户端的目标数据库为1号数据库
注意:谨慎处理多数据库程序,redis没有返回目标数据库的命令,建议显示切换数据库后,再执行其他操作。
在这里插入图片描述

键空间以及生存时间,过期时间

键空间详解

struct redisDb{
   dict *dict; //dict字典保存了数据库中所有的键值对,称为键空间
}

redisDb保存了所有键空间,键空间和用户所见的数据库是直接对应的。

  1. 键空间的键就是redis数据库的键,每个键都是一个String对象
  2. 键空间的值就是redis数据库的值,每个值可以是五种基本对象
    举例:所有的对象都嵌套了字符串对象
set message "hello"
rpush alphabet "a" "b" "c"
hset book name "redis"
hset book author "Carlson"
hset book publisher "Manning"

在这里插入图片描述
通过键空间(dict字段)实现的指令:flushdb,dbsize,exists,rename,keys,

设置键的生存时间

  1. 设置键的生存时间
    所有设置键生存时间的命令,最后都转化为pexpireat
## 设置key的生存时间为5秒
expire key 5     
## 设置key的生存时间为5毫秒
pexpire key 5
## 设置key的生存截止时间
expireat key UNIX时间戳
## 设置key=true,生存时间为5秒
set key true ex 5 nx
## 返回这个键的剩余生命时间
ttl key  
  1. 保存键的生存时间-过期字典
struct redisDb{
   dict *dict; //dict字典保存了数据库中所有的键值对,称为键空间
   dict *expire; //expire字段保存了数据库键的过期时间,称为过期字典
}

键字典+过期字典
在这里插入图片描述

移除键的生存时间

persist key 移除生存时间

计算键的生存时间,判定过期键

TTL key
  1. 首先检查key是否存在过期字典,存在获取过期时间
  2. 检查UNIX时间戳是否大于key过期时间,如果大于,则过期

过期键的三种删除策略

  1. 定时删除:设置过期时间同时,创建一个定时器
    优点:对内存友好,尽可能快的删除
    缺点:对cpu不友好,若过期键过多,会占用cpu。定时器事件用到时间事件
  2. 惰性删除(被动删除):放任过期键不管,当获取键时expireIfNeeded函数检查是否过期,过期就删除该键
    优点:对cpu友好,只会操作键做过期检查,而不会删除其他无关过期键
    缺点:对内不能不友好,过期键占用内存不释放,类似内存泄漏
  3. 定期删除:每过一段时间,程序对数据库检查,然后删除过期键
    redis周期性serverCorn函数执行时,activeExpireCycle函数就会被调用
    函数从一定数量的db,取出一定数量的随机键进行检查,并删除过期键
    current_db会记录函数检查进度,保证下次执行下一个数据库
    若所有数据库都执行完,current_db重置为0,再开始新一轮检查。

redis删除过期键的策略

  1. redis服务器使用定期删除和惰性删除两种策略,惰性删除策略只在碰到过期键才会进行删除,定期删除策略则每隔一段时间主动删除过期键
  2. 执行bgsave命令创建RDB文件时,新的RDB文件不会包含过期的键;载入RDB由于主从同步不会造成影响
  3. 执行bgrewriteaof重写AOF文件时,新的AOF文件不会包含过期键
  4. 主服务器删除过期后,会向从服务器发送一条del命令
  5. 从服务器发现过期键不会自己删除,而是等待主服务器发del命令。这种中心化的删除策略保证主从一致性
  6. redis命令对数据库修改后,服务器会根据配置向客户端发送通知。

RDB持久化

在这里插入图片描述
RDB持久化是将redis的内存数据保存到磁盘里面来进行持久化的。

RDB持久化的实现

  1. 手动执行,也可以根据配置定期执行RDB持久化
    save命令会阻塞redis服务器进程,直到RDB文件创建完毕为止,导致服务器不能处理任何请求。
    bgsave命令会生成一个子进程,子进程负责创建RDB文件,父进程继续处理命令请求
  2. 何时载入RDB文件
    服务器启动时会自动检测到RDB文件,然后自动载入。
    由于AOF文件更新频率比较高,如果服务器开启AOF持久化功能,服务器会优先使用AOF文件恢复数据。
  3. bgsave执行时,载入时的限制
    bgsave执行时拒绝执行bgsave,save命令,但是会延迟执行bgrewriteaof命令,反过来则拒绝执行
    服务器载入RDB期间,会一直处于阻塞状态,直到载入完成

自动间隔保存

##服务器在60秒内对数据库至少做了10000次修改
##配置文件or传入启动参数
save 900 1
save 300 10
save 60 10000 
  1. 服务器会根据save选项设置redisServer的saveparams中seconds和changes
  2. 当服务器成功执行数据库修改命令后,会累加dirty计数器
  3. serverCorn默认每隔100毫秒就执行一次,检查saveparams中的条件是否满足,满足则执行bgsave并记录lastsave属性

RDB文件结构

在这里插入图片描述
在这里插入图片描述

REDIS5字节字符:表示此文件为RDB文件
db_version:记录rdb文件的版本号
databases:保存多个数据库的数据
EOF:标志着rdb文件正文的结束
check_sum:效验和,检查RDB文件是否出错
在这里插入图片描述
SELECTDB:标识下一个字节是数据库号码
db_number:数据库号码
key_value_pairs:保存数据库所有键值对
在这里插入图片描述
在这里插入图片描述
type:保存键的底层数据结构
EXPIRETIME_MS:表示下一个字节为过期时间

  1. 字符串对象
    在这里插入图片描述
  2. 列表对象
    在这里插入图片描述
  3. 集合对象
    在这里插入图片描述
  4. 字典对象
    在这里插入图片描述
  5. 有序列表
    在这里插入图片描述

RDB文件总结

  1. 用于保存和还远redis数据库中所有键值对数据
  2. save命令服务器进程直接保存,阻塞服务器,bgsave由子进程保存,不会则色服务器
  3. 由save选项设置保存条件,当任意条件满足时,服务器自动执行bgsave命令
  4. rdb文件是一个经过压缩的二进制文件,对于不同的键值对,使用不同方式来保存它们。

AOF持久化

AOF是通过记录写命令来对redis进行持久化的。
例子:

set msg "hello"
sadd fruit apple banana cherry
rpush numbers 1 1 2
RDB持久化保存:msg,fruit,numbers这三个键值对
AOF持久化保存:保存set,sadd,rpush这三个命令

AOF持久化的实现

命令追加——>文件写入与同步

  1. 命令追加
    服务器执行写命令后,将写命令追加到服务器aof_buf缓冲区的末尾
  2. 文件写入与同步
    redis的服务器进程就是一个事件循环:文件事件负责接收命令请求,向客户端回复。时间事件执行serverCorn定时函数等
    当写命令追加到aof_buf中后,通过appendfsync不同值产生不同行为。
    若等于always,则在每个事件循环将aof-bug所有数据都写入并同步到AOF文件
    若等于everysec,则在每个事件循环将aof-buf写入aof文件缓冲区,然后每隔1秒同步到AOF文件;
    若等于no,在每个事件循环将aof-buf写入aof文件缓冲区,何时同步由os决定

    解决写入数据带来的安全问题:
    使用fsync和fdatasync手动 将数据从缓冲区同步到磁盘,确保写入数据的安全性

AOF数据恢复

创建伪客户端,从AOF文件循环读出写命令让伪客户端执行,直到所有写命令执行完毕为止。

解决AOF文件文件体积膨胀问题

为了解决AOF文件体积膨胀问题:例如服务器对list键执行了100次rpush操作,挤压保存100个指令。
AOF创建新AOF文件替代现有AOF文件,并保证不会包含任何冗余命令,达到AOF文件瘦身的目的。
执行bgrewriteaof步骤:

  1. 创建新的AOF文件
  2. 遍历redis数据库的所有键,然后根据键的类型进行重写
  3. 为了避免输入缓冲区溢出,在操作集合类键类型时,会先检查是否超过REWRITE_ITEM_PER_CMD
  4. 服务器进程除了执行客户端发来的命令外,还要将执行后的写命令追加到AOF缓冲区,AOF重写缓冲区
  5. 子进程完成重写工作后,向父进程发信号,然后父进程将AOF重写缓冲区中的内容写入新AOF文件中,然后通过改名替换现有AOF文件

事件机制

redis服务器是一个事件驱动程序。基于Reactor模式开发了网络事件处理器(文件时间处理器)
文件事件:redis服务器通过套接字与客户端进行连接,文件事件就是对套接字操作的抽象,服务器通过监听并处理事件完成一些列网络通信操作。
时间事件:时间事件就是对定时操纵的抽象

文件事件

文件事件处理器使用I/O多路复用程序同时监听多个套接字,并根据套接字执行的任务,为套接字关联不同的事件处理器。
文件事件处理器={套接字,IO多路复用程序,文件事件分配器,事件处理器}
在这里插入图片描述
IO多路复用程序总是将发生事件的套接字 放到一个队列中,然后依序 传递到文件事件分派器
在这里插入图片描述

事件的类型

IO多路复用程序可监听多个套接字的readable和writable事件
客户端执行write,close操作时,套接字产生ae_readable事件
客户端执行read操作时,套接字产生ae-writable事件
若一个套接字同时产生了这两种事件,那么文件事件分派器优先处理ae_readable事件,在处理ae-writable事件。

文件事件处理器

程序会将文件事件处理器 与 服务器监听套接字的ae_readable事件关联起来

  1. 连接应答处理器:对连接服务器监听套接字的客户端进行应答
  2. 命令请求处理器:
  3. 命令回复处理器
    客户端向服务器发送连接请求,服务器执行连接应答处理器
    客户端向服务器发送命令请求,服务器执行命令请求处理器
    服务器产生命令回复,服务器执行命令恢复处理器。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值