Redis与MySQL设计_Redis设计与实现——单机数据库的实现

数据库

服务器中的数据库

76a2733c0ec5eeed19fde76c59c68854.png

redisClient切换数据库

redis客户端默认目标数据库为0号数据库,可以通过SELECT命令来切换目标数据库。

客户端状态redisClient结构的db属性记录了客户端当前的目标数据库,这个属性是指向redisdb结构的指针。

typedef structredisClient{//记录客户端当前正在使用的数据库

redisDb *db;

} redisClient;

2b880bd2d9644719578d02303556f84a.png

数据库键空间

Redis是一个键值对数据库服务器,服务器中的每个数据库都由一个redis.h/redisDb结构表示,其中redisDB的dict字典保存了数据库中的所有键值对,我们将这个字典称为键空间。

typedef structredisDb{//数据库键空间,保存着数据库中的所有键值对

dict *dict

} redisDb;

键空间和用户所见的数据库是直接对应的:

1)键空间的键也就是数据库的键,每个键都是一个字符串对象。

2)键空间的值也就是数据库的值,每个值可以是字符串对象,列表对象,哈希表对象,集合对象和有序集合对象中任意一种Redis对象。

e.g.

redis > SET message "hello world"ok

redis> RPUSH alphabet "a" "b" "c"

3redis> HSET book namr "Redis in Action"

1redis> HSET book author "Josiah L. Carlson"

1redis> HSET book publisher "Manning"

1

8bc05db196ed970081e5677bfde5400d.png

读写键空间时的维护操作

当使用Redis命令对数据库进行读写时,服务器不仅对键空间执行指定的读写操作。还会执行一些额外的维护工作。

1)对一个键的读取命中次数和未命中次数,在INFO stats命令的keyspace_hits属性和keyspace_misses属性中查看。

2)读取一个键之后,服务器会更新键的LRU时间,这个值可以用于计算键的闲置时间。

3)读取一个键发现键已经过期了,那么服务器会删除这个过期键,然后才执行余下的其他操作。

4)如果有客户端使用WATCH命令监视某个键,被修改之后会记为脏(dirty),让事务程序注意到这修改。

5)每次修改一个键之后,都会对脏(dirty)键计数器的值增1,这个计数器会触发服务器的持久化以及复制操作。

6)键的修改触发数据库通知功能。

设置键的生存时间或过期时间

4221afcbb2c96ea866f92ffe11a7f9ea.png

保存过期键:

typedef structredisDb{//过期字典,保存着键的过期时间

dict *expires;

} redisDb;

dc97d5f8c8e437b251c38f054e449ba8.png

移除过期时间,计算并返回剩余生存时间,过期键的判定类似。

过期键删除策略

三种策略;定时删除,惰性删除,定期删除。

db2ad4a6f6bec7bd2a32dd13c5a4dcf9.png

redis的过期删除策略

Redis服务器实际使用的是惰性删除和定期删除两种策略:通过配合使用这两种删除策略,服务器可以很好地合理使用CPU时间和避免浪费内存空间之间取得平衡。

懒性删除策略的实现:                                                                                                       定期删除策略的实现:

701e0699fca748b141f8d4353336bf8c.png             

a8f8be5fd113f15c3368782654da7552.png

AOF,RDB和复制功能对过期键的处理

RDB持久化:

生成RDB文件将过滤过期键。

载入RDB文件,如果是主服务器模式运行,过滤过期键;如果是从服务器模式运行,则一并载入,主从数据同步会清空从节点数据,所以不会有影响。

AOF持久化:

3a2b60dafc79ca6733a99b4aabeb0d57.png

同样对写AOF文件,会过滤过期键。

复制功能:

a11e4d2727f5f4f263b81c74749b6c69.png

数据库通知

总结

7c1d4153c1a8522ce9c185135156445e.png

RDB持久化

RDB文件的创建和载入

7c90f666b94a15bed69a4a0ac8385768.png

保存RDB二进制文件,使用SAVE,BGSAVE,其中save命令会堵塞进程,而bgsave会启动后台进程。如果启动了AOF持久化,那优先载入AOF日志。

379aecc7f89f92ce8108b8e2a1c69ac4.png

自动间隔性保存

#设置保存条件

save900 1服务器在900秒之内,对数据库进行了至少1次修改

save300 10服务器在300秒之内,对数据库进行了至少10次修改

save60 10000服务器在60秒之内,对数据库进行了至少10000次修改

#实现structredisServer{//计入了保存条件的数组

struct saveparam *saveparam//dirty修改计数器 表示服务器在上次保存后,对数据库状态共进行多少次修改

long longdirty//上一次执行保存的时间

time_t lastsave

}structsaveparam {//秒数

time_t seconds;//修改数

intchanges

}

#检查保存条件是否满足,则每隔100毫秒周期性执行ServerCron函数,遍历条件数组saveparam,对满足条件的数据库,计数器置为0,并更新上次保存时间。

总结:

b407b43717f32722c4c6da210e36390b.png

AOF持久化

与RDB持久化不同的是,aof持久化是通过写命令来保存数据库状态,而RDB保存的是键值对。

b908e4c4164925e5a7cfcafad70be4ab.png

AOF持久化实现

AOF持久化功能分为:命令追加,文件写入,文件同步三个步骤。

命令追加:

structredisServer{//AOF缓冲区 写命令按照一定格式会追加到缓冲区

sds aof_buf;

}

AOF文件的写入与同步:

def eventLoop():whileTrue :

#处理文件事件,接受命令请求以及发送命令回复

processFileEvents()

#处理时间事件 类似于ServerCron定期执行函数

processTimeEvents()

#考虑是否将aof_buf中的内容写入和保存到AOF文件里面,三个选项

flushAppendOnlyFile()

f6801792600f4a540871fa3ba098fecd.png

AOF文件的载入与数据还原

c5f328f3365ef1683de4dbb056071c09.png

AOF文件重写

Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,新旧两个AOF文件所保存的数据库状态相同。

但新的AOF文件不包含冗余命令,所以体积相对较小。

AOF后台重写:

为了解决数据不一致问题,Redis服务器设置了一个AOF重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用。

c6cf390823cec610fc00ee92b76e5c8f.png

当子进程完成AOF重写工作之后,会向父进程发送一个信号,父进程会调用一个信号处理函数并执行以下操作:

1)将AOF重写缓冲区中所有内容写入新AOF文件中,这时新AOF保存的数据库状态==服务器当前数据库状态;

2)对新的AOF文件改名,原子替换旧的AOF文件;

注意:只有信号处理函数执行时会对服务器进程造成堵塞,对性能造成的影响降到最低。

总结:

8347690ce120cafa84ebadce5dee624e.png

事件

文件事件:

c962aca4090efa50b7b17ef4652a89ed.png

I/O多路复用程序总是会将所有产生事件的套接字都放在一个队列里面,并串行化地向文件事件分派器传送套接字。

e10d450483303c1f584e4aa8c5839023.png

时间事件

服务器将所有时间事件都放在一个无序链表中,每当时间事件执行器运行时,它就会遍历整个链表,查找所有已到达的时间事件,并调用相应的事件处理器。

Redis时间事件分为两类:

定时事件:让一段程序在指定的时间之后执行一次。

周期性事件:让一段程序每隔指定时间就执行一次。

事件的调度与执行

5580d36596c08ab7291a883b64c14300.png

总结:

e6e907db22f869b015cb4ee15fc4ae4d.png

客户端

structredisServer{//一个保存所有client的链表

list *clients;

}

c50d4fb3e5d6f67e56a6d63977b7f7ed.png

客户端属性

typedef structredisClient{//套接字描述符

intfd;//名字

robj *name;//标志,记录客户端角色,以及目前所处的状态

intflag;//输入缓冲区 用于保存客户端发出的命令请求

sds querybuf;

//其他 如命令参数,参数个数,输出缓冲区,身份认证,时间

}redisClient;

客户端的创建与关闭

当客户端与服务器通过网络建立连接时,服务器就会调用连接处理事件,为客户端创建相应的客户端状态,并将新的客户端状态添加到服务器状态结构clients链表的尾链。

e1db98e970a07ef73182515781111bfe.png

伪客户端:

Lua脚本的伪客户端

AOF文件的伪客户端

总结:

2a08422bc0048d28dd433d4666d54749.png

cd43c4defd94c9c7d8227f8d44a84653.png

服务端

4ed544a96228a0aae7774b09bc14d8b4.png

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值