1、数据库

struct redisServer{
    
    // 一个数组,保存服务器中所有数据库
    redisDb *db;

    // 服务器的数据库数量,默认16
    int dbnum;
    
};
typedef struct redisClient{

    /** 记录客户端正在使用的数据库
      * 指向redisServer.db数组的其中一个元素*/
    redisDb *db;

} redisClient;
typedef struct redisDb{
    
    // 数据库键空间,保存所有键值对
    dict *dict;

    // 过期字典,保存键的过期时间
    dict *expires;

} redisDb;

默认端口6379;

    数据库的键空间用字典实现;

redisDb结构的expires字典保存了数据库中所有键的过期时间,过期字典的值是一个long long类型的整数,是一个毫秒精度的UNIX时间戳;

    过期键删除策略:【1】定时删除,要创建大量定时器,不现实【2】惰性删除,取出键时才对键进行过期检查,会浪费内存【3】定期删除,定期执行删除函数,从一定数量的数据库中,取一定数量的随机键,删除过期键;Redis使用惰性删除和定期删除两种策略;

如果是从服务器不会检查键是否过期;

    如果使用AOF,当过期键被惰性删除或者定期删除之后,程序会向AOF文件追加一条DEL命令;AOF重写时忽略过期键;

    从服务器的过期键由主服务器控制,从服务器不会删除过期的message键,而是继续将message键返回给客户端,直到主服务器像它发送DEL message命令

 

2、持久化

内存数据库,需要持久化。RDB执行周期长,可能会丢失几分钟的数据,不适合防止数据丢失,但是适合备份数据;AOF可以设置不同的同步策略,丢失数据少,但文件较大

服务器优先使用AOF文件还原数据。

    用户可以设置服务器配置的save选项,每隔一段时间自动BGSAVE,默认配置如下,如果服务器在900s内改动1次,或者300s内改动10次,或者60s内改动10000次,会执行BGSAVE,保存条件的数组保存在redisServer中。

save 900 1
save 300 10
save 60 10000
struct redisServer{
    
    // 记录了保存条件的数组
    struct saveparam *saveparams;

    // 修改计数器
    long long dirty;

    // 上一次保存的时间
    time_t lastsave;

};
struct saveparam{
    
    // 秒数
    time_t seconds;

    // 修改数
    int changes;

};

    redisServer还维护dirty计数器,表示上一次成功执行SAVE或BGSAVE之后的修改计数,lastsave表示上次保存时间;

Redis服务器的周期性函数ServerCron每100 ms执行一次,其中一项工作就是检查dirty是否满足saveparams的条件;

    RDB文件保存的是二进制数据,结构如下,后面两张分别是数据库部分和key_value_pairs部分;

redis 默认数据库设置_服务器

redis 默认数据库设置_客户端_02

redis 默认数据库设置_服务器_03

    (2)AOF:RDB通过保存键值对来记录数据库状态,AOF保存Redis服务器执行的写命令,分为命令追加、文件写入、文件同步三个步骤。

struct redisServer{

    // AOF缓冲区
    sds aof_buf;

};

always、everysec、no三种同步策略。

redis 默认数据库设置_客户端_04

    Redis提供了AOF重写功能解决AOF体积膨胀的问题,适合放到子进程执行,子进程带有服务器进程的数据副本。执行BGREWRITEAOF命令时,服务器维护一个重写缓冲区,在子进程创建AOF文件期间记录服务器的写命令,最后再将缓冲区追加到新AOF文件的末尾;

 

3、事件

Redis是单线程的,对文件事件和事件时间的处理都是同步、有序、原子的。

使用IO多路复用程序同时监听多个套接字的accept、read、write、close操作。多个文件事件会并发出现,IO多路复用程序将产生事件的套接字放入队列。IO多路复用程序的功能通过包装常见的select、epoll、evport、kqueue这些IO多路复用函数库实现,套接字既可读又可写时先读后写。

    事件处理器中最常用的是连接应答处理器(处理accept)、命令请求处理器(read)、命令回复处理器(write);

redis 默认数据库设置_服务器_05

    (2)时间事件,有定时事件和周期性事件两种,目前Redis只有周期性事件,主要是serverCron,所有的时间事件都放在一个无序链表中,时间事件执行器运行时要遍历链表,但链表中通常只有serverCron一项。serverCron每100 ms执行一次,主要工作包括【1】更新服务器的各类统计信息,时间、内存占用、数据库占用情况等【2】清理过期键【3】关闭和清理连接失效的客户端【4】尝试进行AOF或RDB持久化【5】如果是主服务器,定期对从服务器进行同步【6】如果处于集群模式,进行定期同步和连接测试;

 

4、客户端

struct redisServer{
 
    // 一个链表,保存了所有客户端状态,即List<redisClient>
    list *clients;

};
typedef struct redisClient{

    // 套接字描述符,是伪客户端时为-1,即AOF文件或Lua脚本的情况,不需要套接字连接
    int fd;

    // 客户端名字,可以用CLIENT setname命令设置
    robj *name;

    // 标志,表示客户端的角色和状态,比如是否主从、阻塞、在执行事务、处于集群下等等
    int flags;

    // 输入缓冲区,保存客户端的命令请求
    sds querybuf;

    // 命令的数组,例如{"set","key","value"}
    robj **argv;

    // argv数组长度
    int argc;

    // 指向要执行的命令,服务器查找命令表获得command,命令不区分大小写
    struct redisCommand *cmd;

    // 固定大小的输出缓冲区,保存命令回复
    char buf[REDIS_REPLY_CHUNK_BYTES];

    // buf数组已使用字节数量
    int bufpos;

    // 可变缓冲区,链表实现
    list *reply;

    // 是否通过身份认证
    int authenticated;

    // 创建客户端的时间
    time_t ctime;

    // 与服务器最后一次互动时间
    time_t lastinteraction;

    // 客户端空转时间
    time_t obuf_soft_limit_reached_time;
 
} redisClient;

伪客户端有载入AOF文件和Lua脚本两种情况,无需套接字;

 

5、服务器

根据argv[0]在命令表里查找指定命令,保存在redisClient的cmd属性里,命令名字的大小写不影响命令表的查找结果。

    serverCron管理服务器资源,如下:

    (1)更新服务器时间缓存,更新redisServer的time_t unixtime(秒级精度时间戳)和long long mstime(毫秒级精度时间戳)属性;

    (2)更新LRU时钟,即redisServer里的unsigned lruclock,默认每10s更新一次,用于计算键的空转时长;

    (3)更新服务器每秒执行命令次数;

    (4)更新服务器内存峰值记录;

    (5)处理SIGTERM信号,是关闭服务器的信号;

    (6)管理客户端资源;

    (7)管理数据库资源,过期检查、扩容、收缩等;

    (8)执行被延迟的BGREWRITEAOF,执行BGSAVE期间的BGREWRITEAOF命令会被延迟执行;

    (9)检查持久化操作的运行状态;

    (10)将AOF缓冲区的内容写入AOF文件;

    (11)关闭异步客户端,关闭输出缓冲区超出限制的客户端;

    (12)增加cronloops计数,表示serverCron函数的执行次数;