redis

redis是用C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库。

redis的对象系统

Redis使用数据结构来实现对象,然后由对象系统实现健值数据库。

创建k-v对象

每当我们在redis创建一个键值对时,就会创建两个对象,一个键对象(为sds对象),另一个对象类型将会根据存储类型自动选择,值对象可以是字符串对象,列表对象,哈希对象,集合对象,有序集合对象。

redisObject对象

struct redisObject{
	type类型
	encoding实现的数据结构
	ptr指向数据结构
	refcount引用计数
	lru//最后一次被访问的时间
}

对象的引用计数refcount
创建一个对象,时候refcount=1
当refcount=0时候,对象占用的内存会被删除
当对象被另一个个数据库的键值对的对象指向时,refcount+1
redis会在启动时候创建1000个整数对象,用于对象共享。
不共享字符串对象,因为对比比较花时间。只用于整数。

在这里插入图片描述

在Redis中,哈希类型是指v(值)本身又是一个键值对(k-v)结构

127.0.0.1:6379> exists book
(integer) 0
127.0.0.1:6379> hset book title "Mastering Redis" // 当键不存在时,会新建一个哈希表并设置域的值
(integer) 1
127.0.0.1:6379> hget book title
"Mastering Redis"
127.0.0.1:6379> hset book title "Thinking In Java"  // 当域已经存在于哈希表时,会用新值覆盖旧值
(integer) 0
127.0.0.1:6379> hget book title
"Thinking In Java"

Redis 的 Set 集合数据 , 与 List 列表功能相似 , 唯一的区别是 Set 集合中的元素是不允许重复的 ;

127.0.0.1:6379> sadd name Tom Jerry Jack
(integer) 3
127.0.0.1:6379> smembers name
1) "Jerry"
2) "Tom"
3) "Jack"

redis的数据结构

Redis有以下这五种基本类型:

  • String(字符串)
  • Hash(哈希)
  • List(列表)
  • Set(集合)
  • zset(有序集合)

对象的value的type:

查询一个对象的type方法:type 对象的key

对象对象type属性的值TYPE命令的输出
字符串对象REDIS STRING“string”
列表对象REDIS LIST“list”
哈希对象REDIS HASH“hash”
集合对象REDIS SET“set”
有序集合对象REDIS ZSET“zset”

对象的值的encoding:

查询方法:object encoding 对象的key

编码底层数据结构
REDIS_ENCODING_INTlong 类型的整数
REDIS_ENCODING_EMBSTRembstr 编码的简单动态字符串
REDIS_ENCODING_RAW简单动态字符串
REDIS_ENCODING_HT字典(一对哈希表,方便rehash)
REDIS_ENCODING_LINKEDLIST双端链表
REDIS_ENCODING_ZIPLIST压缩列表
REDIS_ENCODING_INTSET整数集合
REDIS_ENCODING_SKIPLIST跳跃表和字典
  • String:如果存储数字的话,是用int类型的编码;如果存储非数字,小于等于39字节的字符串,是embstr;大于39个字节,则是raw编码。

embstr和raw都是sds的编码实现,embstr是专门用来保存短字符串的一种优化编码方式,他和raw一样都是使用了sds和redisObject结构来表示字符串对象,但是raw会调用两次内存分配函数来分别创建redisObject和sds对象,而embstr则只使用一次内存分配函数来分配一个连续的空间来依次存储redisObject和sds结构。因为是连续空间所以需要保证放置的是小字符串。执行命令时两者效果相同。embstr的优点包含:一次内存分配释放,连续存储方便读取。

  • List:如果列表的元素个数小于512个,列表每个元素的值都小于64字节(默认),使用ziplist编码,否则使用linkedlist编码
  • Hash:哈希类型元素个数小于512个,所有值小于64字节的话,使用ziplist编码,否则使用字典编码。

ziplist存储hash的key-value,采用key-value交替存储的方式,k-v-k-v-k-v-eof

  • Set:如果集合中的元素都是整数且元素个数小于512个,使用intset编码,否则使用字典编码。
  • Zset:当有序集合的元素个数小于128个,每个元素的值小于64字节时,使用ziplist编码,否则使用skiplist(跳跃表)编码

type的encoding实现:

数据结构编码方式描述
REDIS_STRINGREDIS_ENCODING_INT使用整数值实现的字符串对象
REDIS_STRINGREDIS_ENCODING_EMBSTR (DS)使用embstr 编码的简单动态字符串实现的字符串对象
REDIS_STRINGREDIS_ENCODING_RAW使用简单动态字符串实现的字符串对象
REDIS_LISTREDIS_ENCODING_ZIPLIST使用压缩列表实现的列表对象
REDIS_LISTREDIS_ENCODING_LINKEDLIST使用双端链表实现的列表对象
REDIS_HASHREDIS_ENCODING_ZIPLIST使用压缩列表实现的哈希对象
REDIS_HASHREDIS_ENCODING_HT使用字典实现的哈希对象
REDIS_SETREDIS_ENCODING_INTSET使用整数集合实现的集合对象
REDIS_SETREDIS_ENCODING_HT使用字典实现的集合对象
REDIS_ZSETREDIS_ENCODING_ZIPLIST使用压缩列表实现的有序集合对象
REDIS_ZSETREDIS_ENCODING_SKIPLIST使用跳跃表和字典实现的有序集合对象

redis数据库

redis有服务器和客户端,我们使用redis-cli启动就是客户端。

struct redisServer{
redisDb *db;//一个数组,存储着服务器中所以的数据库
int dbnum;//服务器中数据库的数量,初始值为16,默认创建16个数据库
};

默认使用0号数据库,可以使用select切换.

struct redisDb{
dict *dict;
}

在数据库中有一个字典,保存数据库所以的键值对,我们称这个字典为键空间。
字典的键一定是sds对象。

读写键空间时的维护操作

(1)更新服务器的键空间命中次数。
(2)读取一个键后,会更新键的LRU(最后一次使用时间)。
(3)watch
(4)键过期删除
(5)持久化

过期时间如何实现:每个数据库都有过期字典

过期时间是一个long值,是一个unix时间戳
struct redisDb{
dict * expires;//存储着过期时间的字典
}
字典,键是字符串key,值是过期时间。

过期键删除策略

redis使用惰性删除和定期删除的结合。
惰性删除:在每一次读写数据库都会先检查这个键是否过期,如果过期就删除。
定期删除:(1)在每一次取出都会捎带一些键检查(2)会顺序检查

RDB文件对过期时间的处理

保存:在执行save或者bgsave命令创建一个新RDB文件时,不会保存过期的键。
载入:服务器以主服务器运行,会检查键的过期时间,如果过期不会载入。如果是从服务器,过期也会载入,但是主从同步后就没了。

AOF文件对于过期时间的处理

AOF文件的写入:当键过期后,会在AOF中写入一条delet命令。
AOF的重写:会忽略过期键。

分布式的过期时间的实现

主服务器过期后删除,会向从服务器发送delet命令。
在没有主服务器发送的delet命令,从服务器不会处理过期键。

数据库通知:获取键的操作,以及命令的执行情况

(1)键空间通知:查看这个0号数据库和宗的键message执行过什么操作:subscribe__keyspace@0__:message
(2)键事件通知:查看0号数据库中del执行情况:subscribe keyevent@0:del

redis的RDB的持久化:RDB文件包含了数据库在某个时间点上的快照

RDB文件的二进制格式包含了数据库中的几乎所有内容

Redis是一个内存数据库,如果断电就会丢失,通过存储在磁盘上一个二进制文件RDB文件实现持久化。
有两个命令实现持久化:save(阻塞Redis服务进程实现持久化)和bgsave(派生子进程实现持久化)
Redis在启动时候自动执行载入RDB文件。

因为AOF文件的更新频率高于RDB,所以启动时候先检测AOF是否启动,如果AOF启动,则会载入AOF文件,而不用RDB。只有AOF持久化功能关闭才会使用RDB。

当服务器save时候会阻塞,不执行命令。

自动间隔性保存

struct redisServer{
saveparams*  ;
}//redis服务器数据结构包含save参数

Redis允许用户设置save选项,让服务器每隔一段时间自动执行一次bgsave命令。
eg. save 900 1
如果在900秒内执行了至少一次修改,那么就保存。
save设置的默认项:
save 900 1
save 300 10
save 60 1000

redis服务器的数据结构记录了上一次save操作的记录

struct redisServer{
long dirty;
time_t lastsave;
}
dirty表示在上一次成功执行save或者bgsave命令之后,服务器对数据库进行了多少次修改。
lastsave是一个UNIX时间戳,记录了服务器上一次执行save或者bgsave的时间。

周期性检查save条件是否满足

每隔100毫秒就会检查一次是否满足saveparams的条件

RDB文件结构

大写是常量,小写是变量和数据,通过固定长度的字符串和常量实现字段的划分
REDIS–db_version—SELECTDB—0–pairs—SELECTED—3—pairs—EOF----check_sum
pairs存什么就是什么

redis的AOF的持久化

AOF(Append Only File)通过保存Redis服务器执行的写命令来记录数据库的。

AOF的实现步骤可以分为:命令追加(append)到缓存区,写入到AOF磁盘文件。

redisServer{
sds aof_buf;追加到这个缓存区
};

每次用户的命令都会写入到aof的缓存区中

AOF文件的写入

Redis服务器的进程就是一个事件循环,在这个循环中文件事件负责接受客户端的命令请求,以及向客户端发送回复,而时间事件负责定时执行的任务(比如RDB),然后执行AOF写入。

loop:
while True:
processFileEvents();
processTimeEvents();
flushAppendOnlyFile();//根据配置是否需要写入AOF文件

写入AOF文件的策略:每次循环都会将命令写入到aof缓存中,但是写入AOF文件依据同步策略来决定

(1)always:每个命令都在写入aof缓存后都同步到AOF文件
(2)everysec:每过1秒,就同步
(3)no:没有同步命令,每次都需要执行flushAppendOnlyFile()从而同步。

AOF重写

AOF重写文件和原AOF文件无关,是通过从数据库中读取数据,忽略过期键值对,然后生成AOF命令。
是通过创建子进程来进行重写,服务器还可以处理命令,但是这也会导致数据不一致。
Redis服务器设置了一个AOF重写缓存区,AOF执行后会将AOF重写缓存中的命令添加到AOF文件中。

Redis是单线程的,但为了高效持久化与复制,通常会创建子进程:

(1)RDB持久化
(2)AOF持久化
(3)主从复制

redis事件

Redis服务器是一个事件驱动程序,服务器需要处理以下两类事件:
(1)文件事件:Redis客户端(或者其他服务器)通过套接字和Redis服务器通信的抽象。
(2)时间事件:循环事件(或者在指定时间)执行的操作的抽象。

网络事件处理器(文件事件处理器)

文件事件处理器使用IO复用程序来监听多个信道,如果有事件,就派送到相应的事件处理器,执行完毕后,IO复用程序会切换到其他套接字。
当客户端准备好执行连接,读取,写入,关闭等操作时,对应的文件事件就会产生,这时服务器的处理器就会调用相应的处理器来进行处理。

文件事件处理器的构成:文件事件就是对套接字操作的抽象

套接字s1,s2,s3--------------IO多路复用程序---------文件事件分配器--------------事件处理器
事件处理器:命令请求处理器,命令回复处理器,连接应答处理器

事件处理器的类型

服务器相对于套接字:writable事件(客户端读时候,服务器写时候),readable事件(从客户端写时候,服务器读时候)
io复用程序同时监听两种事务,同时发生时候,会先执行readable事件

连接应答处理器----服务器对于套接字的连接监听,实现与客户端的连接

命令请求处理器-----读套接字传入的命令

命令回复处理器----服务器通过套接字给用户回复

一次完整的事件处理过程:

客户端------》连接请求--------》服务器 连接请求应答处理器
客户端------》命令--------------》服务器 命令请求处理器
客户端-------《回复-------------《服务器 命令回复处理器

事件事件

两类:定时事件,周期性事件
区别在于事件的返回值,如果为0是定时事件,如果为30,则会重新启动事件,并且将启动事件设置为30毫秒之后。
这个版本Redis只有周期性事件,而没有定时任务。

时间事件的实现:时间事件处理器

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

时间事件的构成

time_event
id:3
when:12234932423(UNIX)
handle:handle_

代码实现:processTimeEvents()

for()遍历
if(到点了)执行处理器

时间事件应用实例:serverCron函数

redis服务器需要定期对自身资源和状态进行检查:serverCron函数
心跳检测,服务器同步,AOF或RDB,清理过期键,更新服务器统计状态,关闭失效服务器连接
默认,serverCron每100ms运行一次,可以修改。

事件的调度:服务器如何调度文件事件和时间事件

aeProcessEvents():
计算最近的时间事件到达时间
如果已到,则取出来
aeApiPoll(取出的事件);阻塞并等待文件事件的产生,如果到了时间事件的时间,则立刻跳出
processFileEvents();
processTimeEvents();

main():
init_server();
while(not_shutdown)
aeProcessEvents()
close_server();

客户端

每个客户端连接Redis服务器,都会建立一个客户端的数据结构。
客户端向服务器发送请求,服务端返回答复。
Redis服务器使用单线程,多个客户端使用IO复用来实现。

struct redisServe{
list *clients;
}
struct redisClient{
int fd;//套接字描述符
name;
int flags;//客户端状态标志符
sds querybuf;//输入缓存区
robj *argv;//客户端传入的命令数组
int argc;//命令的数量
struct redisCommand cmd;//命令表,装有redis能执行的命令,将会将传入的命令的参数放到命令结构中
char buf【】;//输出缓存区
int authenticated;//身份状态
time_t ctime; //创建客户端过了多久
time_t lastinteraction;//上次执行过了多久
time_t obuf_soft_limit_reached_time;//空转了多久
}

服务器在载入AOF文件时候,会创建一个伪造的Redis服务器来执行命令,执行完后删除。

服务器

用户----命令----> 客户端 ------将命令转换成协议格式然后发送-------> 服务器------回复转换为协议格式------>客户端—将回复显示在界面。
1.
当客户端与服务器之间的连接套接字因为客户端的写入而可读时:
(1)读取套接字请求,并将其保存到redisClient输入缓存区;
(2)分析buf,提取关键字到client的argv;
(3)调用执行器;
2.命令执行器的执行过程
(1)根据client查找相应的命令实现。收集执行函数,执行参数,参数个数。
(2)执行预备操作。
(3)调用命令实现函数。
(4)执行后续操作。
3.serverCron函数
服务器的serverCron函数默认每隔100ms执行一次,这个函数负责管理服务器资源,并且保证服务器的运转。
它的功能有:
(1)更新服务器事件缓存。
(2)更新LRU时钟。最后一次被访问的时间
(3)更新服务器每秒执行命令次数
(4)更新内存峰值
(5)处理SIGTERM信号(关闭服务器的)
(6)管理客户端连接状态以及资源。
(7)接受客户端的bgrewriteaof(aof延迟重写)持久化操作
(8)检查执行持久化
服务器没有在执行如何持久化—》有aof重写----》延迟持久化
服务器没有在执行如何持久化—》没有aof重写—》自动保存条件满足-----》bgsave,rdb持久化
服务器没有在执行如何持久化—》没有aof重写—》自动保存条件没满足----》aof重写满足—》aof重写
(9)aof缓存区写入AOF文件

4.初始化服务器
在redis服务器启动时候,需要初始化服务器
(1)初始化服务器状态,创建与一个redsiServer,设置端口,运行频率,文件路径,rdb和aof条件
(2)载入配置,改为用户配置
(3)初始化数据结构
(4)还原数据库状态,如果开启了AOF,使用AOF,如果没有,就使用RDB。
(5)执行loop,开始工作

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值