Redis设计与实现笔记--单机数据库(二)

1.数据库

默认情况下,redis初始化会创建16个数据库

键空间是个字典,所有数据库操作都是对这个字典进行操作

读写时的维护操作

          读取一个键后(读写操作都要先读),更新键命中次数或者不命中次数

          读取一个键后,更新键的LRU时间

          读取时发现键过期,会先删除这个键

          WATCH命令监视时,对键修改,会标记为脏

          修改键后,对脏键计数+1,这会触发服务器持久化以及复制操作

          键修改,会通知其他服务器

设置过期时间

EXPIRE 生存时间以秒为单位

PEXPIRE生存时间以毫秒为单位

EXPIREAT指定过期的秒数时间戳

PEXPIREAT指定过期的毫秒数时间戳

以上4种方法最终都是转换成PEXPIREAT命令来实现

键空间和过期字典的键都会指向同一对象过期时间是longlong类型

过期键判定

先检查是否在过期字典中,取得过期时间,再跟当前UNIX时间戳比较

过期键删除策略

       定时删除:创建定时器,到过期时间就马上删除

       惰性删除:读取到建时,检查过期时间,这时已经过期才删除

       定期删除:每隔一段时间对数据库检查,删除过期键

Redis实际使用惰性删除和定期删除结合的方法

RDB对过期键处理

SAVE或者BGSAVE会对所有键检查,不会把过期键写入

主服务器载入RDB时也会检查键是否过期,而从服务器是直接载入,但在主从同步时要清空从服务器,所以也没有影响

AOF对过期键处理

AOF模式下,当过期键被惰性删除或者定期删除后,会向AOF文件追加DEL命令

AOF重写时也会忽略过期键

主从模式下不同的返回

客户端访问从服务器,就算键过期了,也会返回原值

当访问主服务器,键过期才会删除,返回null,然后通知从服务器删除

这样保证主从服务器数据的一致性

2.RDB持久化

AOF关闭下,会载入RDB,载入时一直阻塞

SAVE命令下,服务器会被阻塞,所有请求都拒绝。

BGSAVE命令下,在子进程中进行,会拒绝SAVE和BGSAVE命令,BGREWRITEAOF不会和BGSAVE同时进行

BGSAVE可以设置条件自动进行,根据dirty计数器,lastsave属性

lastsave是UNIX时间戳,记录上次SAVE或者BGSAVE时间

dirty记录上次SAVE或者BGSAVE到现在被修改的次数

周期性函数serverCron默认每100毫秒检查是否需要BGSAVE

RDB文件结构

开头REDIS部分,保存“REDIS”5个字符,用于标识RDB文件

db_version表示文件版本号

databases就是保存的数据库数据内容

check_sum是一个8字节长的无符号整数,是前面4个属性的校验和

databases部分

假设0和3数据库非空

pairs保存一个及以上的键值对

TYPE决定如何读入value,key总是字符串对象

EXPIRETIME_MS常量1字节,标识符

ms是8字节的带符号整数,记录时间戳

value编码

->字符串对象

①整型编码

②raw编码

->列表对象

linkedList编码

->集合对象

HT编码

->哈希表对象

HT编码,跟集合对象一样

->有序集合对象

skiplist编码

特殊编码

intset编码,写入和载入会在字符串和整型之间切换

ziplist编码,值为压缩对象,写入时会还原成字符串对象然后写入,读入时再转换成原来的压缩列表对象

 

3.AOF持久化

通过保存修改命令来记录数据库状态,所有写入文件的命令都是由请求协议格式保存

持久化时将命令追加到服务器状态的aof_buf缓冲区末尾

每次一个事件循环结束前会调用flushAppendOnlyFile函数,

always相当于总是直接写到文件中,是安全的

everysec最快隔1秒同步一次,是不安全的,最少丢失1秒的数据,默认使用

no非常不安全,一般不建议这么做

AOF载入过程

AOF重写

aof_rewrite命令,会造成阻塞

在数据库中找到的键值对,直接用一条命令去记录,

BGREWRITEAOF命令

优点:

       在子进程中重新,父进程可以继续处理命令请求

       子进程有父进程数据副本,可以在避免锁的情况下保证安全性

但这也会造成数据不一致的问题,redis设置了AOF重写缓冲区来解决这个问题

父进程会把命令同时给AOF缓冲区和AOF重写缓冲区

AOF重写完成后,发给父进程一个信号,会执行以下操作:

       AOF重写缓冲区中所有内容写入到新的AOF文件中

       对新文件进行改名,原子地覆盖现有AOF文件,完成替换

只有在信号处理函数执行上述操作时才会造成阻塞

 

4.事件

文件事件

虽然文件时间处理器单线程运行,通过使用I/O多路复用来监听多个套接字,

实现高性能网络通信模型(这也是redis吞吐量高的原因)。

I/O多路复用程序会将套接字放在队列中,有序、同步、每次一个的方式向文件事件分派器传送套接字

AE_READABLE事件和AE_WRITABLE事件

I/O多路复用程序可同时监听上述俩个事件,如果套接字又可读又可写,先处理读套接字

时间事件

主要分为:定时事件和周期性事件

id为全局标识,新事件id号比旧事件id号大

when毫秒精度UNIX时间戳,到达时间

timeProc时间事件处理器,一个函数

区分定时事件还是周期性事件看处理器的返回值:

返回AE_NOMORE则为定时事件,该事件在到达后删除

返回非AE_NOMORE则为周期性事件,根据返回值对when属性进行更新

实现

所有时间事件都放在一个无序链表,每次执行会遍历链表。

虽然要遍历整个链表,但几乎退化成指针来使用,所以不影响性能

serverCron函数

通过该函数,redis服务器对自身资源和状态进行检查和调整

①更新服务器的各类统计信息,时间、内存占用、数据库占用等

②清理数据库中的过期键值对

③关闭和清理连接失效的客户端

④尝试进行AOF或者RDB持久化操作

⑤如果是主服务器,那么对从服务器定期同步

⑥如果是集群模式,对集群进行定期同步和连接测试

事件调度与执行

因为文件事件是随机出现的,如果处理完一次文件事件后,未有时间事件到达,则会继续等待文件事件

事件处理是同步、有序、原子的,都会尽可能减少阻塞,如果写入较多则会主动break下次再写

如果时间事件持久化非常耗时会在子线程或者子进程执行

时间事件在文件事件之后执行时,通常会比预设的到达时间稍晚一些

5.客服端

客户端属性

fd表示客户端类型,

伪客户端fd为-1,不需要套接字连接,载入AOF文件和执行Lua脚本会用到伪客户端

载入AOF的伪客户端载入完毕就关闭,Lua脚本伪客户端在整个生命周期都存在

普通客户端fd大于-1的整数,记录客户端套接字的描述符

name,默认情况下没有name,如果有,name字段指向一个字符串对象

flags客户端角色

querybuf输入缓冲区,是个sds,如果大于1GB,会关闭这个客户端

argv是个数组,每一项都是字符串对象,argv[0]要执行的命令,之后是命令的参数

argc记录argv数组长度

cmd指向对应的实现函数

固定缓冲区,buf[REDIS_REPLY_CHUNK_BYTES]字节的数组,bufpos属性记录已使用的字节数量

REDIS_REPLY_CHUNK_BYTES默认为16*1024,大小为16KB

当固定缓冲区不够用时,会使用可变大小缓冲区

可变大小缓冲区由reply链表和一个或多个字符串对象组成

authenticated身份验证,0表示未通过,1表示已通过

ctime记录创建客户端时间,用于计算已连接多少秒

lastinteraction记录最后一次互动的时间,可以用来计算空转时间

obuf_soft_limit_reached_time记录输出缓冲区第一次到达软性限制时间

 

6.服务器

发送命令请求

客户端将命令请求转换成协议格式,通过连接到服务器的套接字将命令发送给服务器

读取命令请求

①读取协议格式的命令,放入客户端状态的输入缓冲区

②对缓冲区分析,提取命令参数和数量,放入argv和argc中

③调用命令执行器,执行命令

命令执行器

①根据argv[0]参数,查找命令

②执行预备操作,检查

③调用命令的实现函数

产生的回复保存在输出缓冲区里

④执行后续工作,

如果开启了慢查询日志,检查是否需要添加日志

根据耗时,更新redisCommand中milliseconds属性,将redisCommand中calls计数器+1

如果开启AOF会将刚刚命令写入AOF缓冲区

如果主从模式,会传播给所有从服务器

⑤回复发送给客户端,发送完毕会清空该缓冲区

 

serverCron函数

默认每100毫秒执行一次

更新unixtime属性和mstime属性,但精确度不高,只会用于对时间精确度要求不高的功能上

对于设置键过期时间,慢查询日志,会再次系统调用得到精确的当前时间

更新LRU时钟  √

默认每10秒更新lruclock属性值,这个时钟不是实时,所以是个估计值

更新服务器每秒执行命令次数

以抽样计算的方式估算最近一秒处理命令请求数量

更新服务器内存峰值记录

每次都会查看服务器当前使用内存数量,与stat_peak_memory比较,保存较大的

处理SIGTERM信号  √

会拦截SIGTERM信号,打开shutdown_asap标识,在关闭前作RDB持久化操作

管理客户端资源  √

检查客户端和服务器之间的连接时间,长时间无互动,则释放这个客户端

在上一次执行命令请求后,检查缓冲区大小,如果唱过一定长度,则释放当前缓冲区,新建默认大小的输入缓冲区。

管理数据库资源  √

检查过期键,在必要时对字典进行收缩操作

执行延迟的BGREWRITEAOF

检查BGSAVE或者BGREWRITEAOF都没有在执行,且sof_rewrite_scheduled属性为1

则执行被推迟的BGREWRITEAOF命令

检查持久化操作运行状态  √

AOF缓冲区中的内容写入AOF文件

关闭异常客户端

增加cronloops计数器值

cronloops作用,每N次执行一次指定代码

 

初始化服务器

①初始化服务器状态

②载入服务器配置

③初始化服务器数据结构

④还原数据库状态

⑤执行事件循环

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值