《Redis设计与实现》读书笔记——数据库、持久化、事件、客户端、服务器

本文深入剖析Redis数据库结构、持久化机制、事件处理流程、客户端管理及服务器运作原理。介绍Redis如何利用字典存储键值对,并结合惰性删除与定期删除策略处理过期键。探讨RDB与AOF两种持久化方式的特点及应用场景。解析文件事件与时间事件在Redis单线程模型中的处理方式。阐述客户端连接与交互过程,以及服务器周期性任务管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、数据库

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

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

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

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

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

} redisDb;

    服务器启动时默认创建16个数据库,客户端默认使用0号,可以用SELECT命令切换,默认端口6379

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

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

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

    RDB:执行SAVE或BGSAVE命令会创建一个新的RDB文件。载入RDB文件时,如果服务器以主服务器模式运行,过期键会被忽略,如果是从服务器不会检查键是否过期

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

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

 

2、持久化

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

    (1)RDB:RDB持久化可以手动执行,也可以根据服务器的配置选项定期执行。SAVE和BGSAVE命令可以用于生成RDB文件,SAVE会阻塞服务器进程,BGSAVE会派生子进程,由子进程创建RDB文件。AOF文件的更新频率比RDB高,服务器优先使用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部分;

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

struct redisServer{

    // AOF缓冲区
    sds aof_buf;

};

    命令追加会以协议格式将被执行的写命令追加到redisServer的aof_buf缓冲区的末尾,文件写入就是将缓冲区的内容写入AOF文件,文件同步就是将AOF文件写入硬盘,有always、everysec、no三种同步策略。

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

 

3、事件

    有文件事件和时间事件两种事件,文件事件优先执行,时间事件的实际处理时间通常会晚一些。Redis是单线程的,对文件事件和事件时间的处理都是同步、有序、原子的。

    (1)文件事件,Redis基于Reactor模式开发了自己的网络事件处理器,被称为文件事件处理器,使用IO多路复用程序同时监听多个套接字的accept、read、write、close操作。多个文件事件会并发出现,IO多路复用程序将产生事件的套接字放入队列。IO多路复用程序的功能通过包装常见的select、epoll、evport、kqueue这些IO多路复用函数库实现,套接字既可读又可写时先读后写。

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

    (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;

    客户端使用connect函数连接到服务器时,服务器调用连接事件处理器,创建对应的redisClient,加入到clients链表末尾;如果客户端空转时间超时会关闭,阻塞、订阅时除外;如果客户端输出缓冲区过大,可以用硬性限制(立即关闭)和软性限制(超过软性限制并持续一定时间再关闭);伪客户端有载入AOF文件和Lua脚本两种情况,无需套接字;

 

5、服务器

    客户端发送命令请求,服务器调用命令请求处理器:【1】读取套接字中的协议格式的命令请求,保存到对应的redisClient的输入缓冲区里【2】分析请求,保存在argv和argc里【3】调用命令执行器,根据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函数的执行次数;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值