图片来自《Redis设计与实现》
Redis数据库
redis.server中redisServer.dbnum用于初始化生成多少个数据库,默认
16个,
Select命令选择数据库
redis.client中redis.db指针指向当前正在使用的数据库
1、过时策略
设置过时时间命令
expire如 expire <key> <ttl>
redisDb主要由dict和expires两个字典组成:
dict 为键空间字典,存放该数据库中全部的键对象;expires参数称为过时字典,存放每一个键对应的过时时间
expires的键是一个指针,指向键空间中的某个键对象;值是long long类型的整数,存放过时时间(毫秒UNIX时间戳(chuo))
两步判断键是否过时:
(1)检查给定键是否存在于过时字典;存在,则得到对应的时间戳
(2)获取当前UNIX时间戳,二者进行比较,便可知道是否过时
三个过时键删除策略:
1.
定时删除:使用定时器(基于时间事件,无序链表,复杂度O(N),没法高效处理大量时间事件),定时检查键是否过时,对内存友好的策略
2.
惰性删除:使用键时才去检查该键是否过时,对CPU友好的策略
3.
按期删除:折中的策略,随机检查一部分键的过时时间并删除其中过时的键,难点是肯定删除执行的时长和频率
Redis中采用 惰性删除 和 按期删除 配合使用
因为两种过时键删除策略仍然可能会致使大量过时键堆积在内存,为此,Redis引入的内存淘汰机制(淘汰策略共6种。最重要的是 allkeys-lru)
过时键对RDB、AOF持久化以及复制的影响
RDB:RDB持久化时,会检查键是否过时;RDB载入时,主服务器会检查键是否过时,从服务器忽略,等待主服务器同步时处理
AOF:AOF文件写入时忽略,直到过时键被删除,才向AOF文件追加一条删除过时键的命令
AOF文件重写时,会检查键是否过时
复制:主服务器每删除一个过时键,则显示地向全部从服务器发送一个DEL命令,通知从服务器删除过时键
(这个其实就是主从复制的同步流程?)
2、持久化
Redis的持久化分为两种:RDB持久化 和 AOF持久化
RDB持久化:
一个通过压缩的二进制文件;以键值对形式存储数据库信息,不一样的键值对使用不一样的方式保存
两个命令的区别:SAVE,BGSAVE
save:
阻塞式RDB持久化。服务器线程执行RDB持久化操做,期间不能处理任何命令请求
bgsave:
非阻塞式RDB持久化。
会建立一个子进程用于执行RDB持久化操做,服务器线程仍然能处理命令请求
RDB文件载入
无命令,服务器启动时自动执行;若服务器开启了AOF持久化功能, 会优先使用AOF文件来还原数据库状态
RDB自动间隔性保存
可配置服务器save选项,每隔一段时间自动执行一次bgsave命令
默认知足三个条件之一,则自动执行一次bgsave命令。条件存储在 saveparams 参数中
除saveparams数组外,还存储
dirty计数器 和
lastsave属性;dirty用于计算累积操做次数,lastsave用于和当前时间戳比较,计算通过的时间
三个条件:
900 1;300 10;60 10000
Redis的周期性操做函数 serverCron 默认每间隔100ms执行一次,用于对正运行的服务器维护,其中就包括检查参数条件是否知足
AOF持久化:
经过保存Redis命令来保存数据库信息
以Redis命令请求协议格式保存,Append Only File
三步实现AOF持久化:
(1)命令追加:每执行一个命令,就将命令追加到
aof_buf缓冲区(常量值大小64)末尾
(2)命令写入:服务器每次结束一个事件循环前,都会调用flushAppendOnlyFile函数,考虑是否将缓冲区数据写入AOF文件
(3)命令同步:与flushAppendOnlyFile函数的
appendfsync 参数决定
always:缓冲区写入并同步到AOF文件
everysec:每秒同步一次
no:只写入,由操做系统决定同步
AOF的载入还原:
(1)建立一个不带网络链接的伪客户端(用于执行AOF文件中的Redis命令,由于命令只能在客户端中执行)
(2)从AOF文件中分析并读取出一条命令
(3)伪客户端执行命令
(4)循环执行2,3;直到AOF文件中的命令执行完毕
AOF的文件重写: bgrewriteaof
为何须要:为了解决随时间和操做的增加,AOF文件的膨胀问题,处理AOF文件中命令冗余
实现理念:不是读取原有AOF文件内容,而是直接读取当前数据库状态,而后用一条(或多条,尽可能少的)命令去记录键值对
实现过程:
1.建立一个
子进程后台执行重写 --- 避免使用锁的状况下,保证数据安全;且不阻塞服务器进程
2.使用了AOF重写缓冲区,服务器每执行一个命令,都将命令同时追加到AOF缓冲区和AOF重写缓冲区
3.重写工做完成后,子进程返回一个信号,父进程调用信号处理函数
(1)将AOF重写缓冲区的内容写入新AOF文件中,使新AOF文件与数据库状态一致
(2)对新AOF文件更名,原子性地覆盖现有AOF文件,完成新旧AOF文件交替
注意:调用信号处理函数阶段,服务器进程阻塞。(
重写过程当中就这一处阻塞)
RDB与AOF两种持久化方式的比较
(1)RDB文件小,恢复快;可是没法保存最近一次快照后的数据,即会丢失这部分数据
(2)AOF文件可读性高,数据不易丢失;可是文件体积大,恢复时间长
Redis 4.0 开始支持 RDB 和 AOF 的混合持久化
3、事件
Redis 服务器是一个事件驱动程序
单线程运行,
I/O多路复用来监听多个套接字;有两种事件(文件事件和时间事件)
文件事件:对套接字进行操做的抽象;有可读事件、可写事件;文件事件拥有基于Reactor模式的文件事件处理器。
文件事件处理器: 套接字 + I/O多路复用程序 + 文件事件分派器 + 事件处理器
常见事件处理器:链接应答处理器,命令请求处理器,命令回复处理器。(依次为链接、接收命令、处理命令回复)
时间事件:给定的时间点执行类操做的抽象;有定时事件、周期性事件redis
时间事件实现: Redis 将全部时间事件都放在一个无序链表(不按when属性大小排序)中,经过遍历整个链表查找出已到达的时间事件,并调用相应的事件处理器。数据库
时间事件:三个参数(id,when,timeProc);典型事件:serverCron数组
事件调度与执行
def aeProcessEvents():
# 获取到达时间离当前时间最接近的时间事件
time_event = aeSearchNearestTimer()
# 计算最接近的时间事件距离到达还有多少毫秒
remaind_ms = time_event.when - unix_ts_now()
# 若是事件已到达,那么 remaind_ms 的值可能为负数,将它设为 0
if remaind_ms < 0:
remaind_ms = 0
# 根据 remaind_ms 的值,建立 timeval
timeval = create_timeval_with_ms(remaind_ms)
# 阻塞并等待文件事件产生,最大阻塞时间由传入的 timeval 决定
aeApiPoll(timeval)
# 处理全部已产生的文件事件
procesFileEvents()
# 处理全部已到达的时间事件
processTimeEvents()