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次执行一次指定代码
初始化服务器
①初始化服务器状态
②载入服务器配置
③初始化服务器数据结构
④还原数据库状态
⑤执行事件循环