文章目录
数据库
Redis服务器将所有数据库都保存在redisSever结构的db数组中,db数组的每一项都是一个redisDb结构,代表了一个数据库。一般默认初始化16个数据库。可以通过SELECT
指令切换数据库。
Redis是一个键值对数据库服务器,redisDb结构的dict字典保存了数据库中的所有键值对,将这个字典成为键空间。键空间的键是数据库的键,每个键都是字符串对象。值可以是任意一种Redis对象。并且还有一个expires字典保存键过期信息。
读写键空间是的维护操作
-
miss与hit
在读取一个键之后(包括读操作和写操作),服务器会根据键是否存在,更新服务器中命中率(hit)和不命中(miss)的次数。 -
LRU更新
在读取一个键之后,服务器会更新LRU,用于计算闲置时间。 -
删除过期键
在读取时候发现某个键已经过期,会先删除这个过期键,再执行其他操作。 -
标记dirty
如果由客户端使用WATCH
指令监视了某个键,那么服务器在对被监视的键进行修改的之后,会把这个键标记为dirty,从而让事务处理器注意。 -
dirty计数器增加
服务器每次修改一个键之后,都会对dirty计数器增1.这个计数器会触发服务器的持久化和复制操作。 -
发送数据库通知
如果开启了数据库通知,在对键进行修改之后,服务器会按照配置发送数据库通知。
键的生存过期时间
通过EXPIRE
或者PEXPIRE
可以对某个键只是生存时间,在过了生存时间之后,服务器会删除生存时间为0的键。EXPIRE<key><ttl>
设置生存时间为ttl秒,EXPIREAT<key><timestamp>
某个键的过期时间设置为timestamp指定的秒。
redisDb结构中expires字典保存了数据库中所有键的过期时间,称为过期字典。过期字典的键是一个指针,指向键空间的某个键对象,值是一个long long类型的整数,保存了过期时间(键空间的键和过期字典的键都指向同一个键对象,节省空间)。
PERSIST
可以移除一个键的过期时间,TTL
可以以秒为单位返回键的剩余空间。
过期键的处理
- 过期键的判定
首先检查键是否存在与过期字典,如果存在返回过期时间;检查当前UNIX时间是否大于过期时间。
- 过期键删除策略
- 定时删除:创建一个定时器,在过期时间来临时候删除。内存友好,CPU不友好,并不使用因为时间事件是无序队列, O ( N ) O(N) O(N)复杂度。
- 惰性删除:放任过期时间不管,在再次访问改键时候进行判定。CPU友好,内存不友好。如果不去访问,相当于是一种内存泄漏。
- 定期删除:每隔一段时间对数据库进行检查,删除过期键。折衷的方法。
- Redis中的惰性与定期删除
惰性:所有的命令执行前都会调用expireIfNeed,检查过期情况。因此每个命令的实现函数都需要能执行键存在和不存在两种情况。
定期:由activeExpireCycle执行,redis服务器的周期性程序severCron被执行时,该方法被调用。在规定的时间内,分多次遍历服务器的各个数据库。每次运行时,从一定数量的数据库中选择一定数量的随机键检查并删除过期键,并保存每次的进度,下次再被调用时候继续运行。
AOF,RDB和复制对过期键的处理
- RDB对过期键的处理
在生成RDB文件时候,会检查过期情况,因此RDB文件不会包含过期键。载入过程中,如果是主服务器,会对过期进行检查,过期数据不会被载入,从服务器则全部加载。但是也不会有影响,因为从服务器会被主服务器同步。
- AOF对过期键的处理
AOF生成过程中,如果键已经过期但是没有被显式删除则不会对AOF有影响,在被惰性或者定期删除之后,程序会在AOF文件之后跟一个DEL命令。AOF重写中程序会检查过期情况,因此不会被存入AOF。
- 复制操作对过期键的处理
主服务器在删除一个键之后,会显式的向从服务器发送del指令;从服务在读取到过期键之后,不会进行任何操作而是正常返回,只是为了保持一致。
持久化
Redis时内存数据库,因此速度快,但是我们需要把数据存回硬盘,因此需要持久化。
RDB持久化
RDB持久化功能所生成的RDB文件时一个经过压缩的二进制文件,通过该文件可以还原生成的RDB文件。
RDB文件的创建与载入
两个指令可以生成RDB文件SAVE
和BGSAVE
。SAVE会阻塞Redis服务器进程,直到EDB文件创建完毕。BGSAVE会派生出一个子进程,然后子进程负责创建RDB文件,父进程继续处理命令。
Redis的载入是服务器启动时候自动执行的。需要注意的是,由于AOF的更新频率必RDB高,因此如果服务器开启了AOF持久化,服务器会优先采用AOF来恢复。
BGSAVE执行期间,客户端的SAVE和BGSAVE指令会被拒绝,BGREWRITEAOF会被延迟到BGSAVE执行完毕;如果BGREWRITEAOF正在执行,BGSAVE会被拒绝。
自动保存
一般BGSAVE会在一定条件下自动执行,如900s以内数据库执行了至少一次修改;300s内数据库执行了10次修改;60s内执行了10000次修改。这里依靠了两个全局变量,一个是dirty计数器,统计了上次保存之后,服务器进行了多少次修改,一个是lastsave保存了上次的成功save的事件戳。Redis的周期性函数severCon每100ms执行一次检查,如果满足条件就BGSAVE。
RDB文件结构
redis五个字符,db_version四个字节保存了版本号,EOF是结尾标志,check_sum是检验和。
SELECTDB是一个标识,db_number表示数据库号码。key_value_pairs是键值对。对于不同类型的键值对会不同的保存方式。
AOF持久化
AOF(Append Only File)与RDB直接保存数据库中的键值对来记录数据库的状态不同,AOF持久化是通过保存服务器所执行的写命令来记录数据库状态的。被写入AOF文件的所有命令都是以Redis命令请求协议格式保存的。
AOF持久化的实现
在服务器执行完一个写命令以后,会在服务器状态的 aof_buf 缓冲区末尾追加一个写命令。并定期的同步到AOF文件中。
Redis的服务器进程就是一个事件循环,有两种事件,一个是文件事件负责接收客户端的命令请求,以及发送回复到客户端。另外时间事件负责执行类似severCron函数这样需要定时运行的函数。
因此服务器每次结束一个事件循环之前,都会调用flushAppendOnlyFile的函数,考虑是否将 aof_buf 缓冲区的内容写入AOF文件中。其中分为三个时间参数:always
每次都将缓冲区的全部数据写入AOF文件中并同步;everysec
如果上次同步时间距离现在超过1s,则进行一次同步,这个过程是一个线程专门执行的;no
只是写入AOF但是不同步到硬盘。
AOF载入过程中,回有一个伪客户端执行AOF的命令,直到完成全部恢复工作。
AOF重写
为了压缩AOF文件的大小,redis提供了AOF文件重写功能,可以创建一个新的AOF文件来替代现有的AOF文件,也就是BGREWEITEAOF
。
事实上AOF的重写不需要对现有的AOF文件进行任何的读写,而是用个服务器状态实现。在重写过程中会忽略过期的键。重写名列最多包含64个键值对,否则还是要多个语句。
为了提高性能,redis将AOF的重写程序放在了子进程中执行,这样服务器进程可以正常相应客户端的请求,并且子进程有服务器进程的数据副本,可以保证数据安全。同时为了解决不一致问题,redis服务器设置了一个AOF重写缓冲区,在子进程创建之后,redis的写命令会同时给AOF缓冲区和AOF重写缓冲区。在完成AOF的重写后,子进程给父进程发送一个信号,父进程阻塞并且将重写缓冲区的内容写入AOF文件中,并原子的覆盖原AOF文件,完成整个任务。
事件
redis服务器是一个事件驱动模型,包括文件事件和时间事件。redis服务器通过套接字与客户端进行连接,而文件事件就是服务器对套接字操作的抽象。
文件事件
文件事件处理器被用于处理文件事件。虽然redis是单线程的,但是处理器采用IO多路复用,监听多个嵌套字,提高效率。事件处理器分为四个组成部分,套接字,IO多路复用,文件事件分派器,事件处理器。