Redis原理笔记

7 篇文章 0 订阅


学习《Redis设计与实现》的一些关键点笔记。

点击下载:《Redis设计与实现》提取码: 2hur

数据类型底层数据结构

数据结构

  • 动态字符串(sds )
  • 链表(list)
  • 字典(dict)
  • 跳跃表(skipList)
  • 整数集合(intSet)
  • 压缩列表(zipList)

String

String ->sds

List

List -> list->listNode
List -> zipList
如果List中元素数量比较小,并且值都是小的整数,或者比较短的字符串,会使用zipList

Hash

Hash -> dict->dictht->dictEntry
Hash -> zipList
如果Hash表中,元素数量比较小,并且每个键值对都是小的整数,或者比较短的字符串,就会使用zipList

ZSet

ZSet -> skipList
有序集合包含元素数量比较多,或者有序集合中有比较长的字符串,ZSet就是这时候使用skipList

Set

Set->intSet
如果一个集合只包含整数值,并且集合元素数量不多时,采用intSet结构。

数据对象

redisObject{

Type;
Encoding;// 编码类型

ptr;//数据指针

Refcount;// 引用计数,和内存回收有关

lru;// 记录空转时长,lru的淘汰机制有关

}

数据库

redisServer{

// 数据库数量(默认16个),也就是下面数组的长度
int dbNum;

// 一个数组,保存所有数据库
redisDb *db;

// 一个数组,记录RDB持久化条件
savaParam *saveParams;

// AOF缓冲区
Sds aof_buf;

}
redisClient{

// 客户端当前使用的数据库
redisDb *db;

}

redisDb{

// 数据库字典,保存着数据库中所有键值对
Dict *dict;

// 过期数据字典,保存数据库所有 设置了过期时间的键,过期时间是Unix时间戳
dict *expires;

}

过期键删除策略

redis实际使用的是惰性删除和定期删除两种策略。可以很好合理利用CPU和内存之前的平衡。

  • 定时删除:为key设置过期时间的同时,创建一个定时器,在key过期时立即删除。
    对内存友好,但是浪费CPU时间,影响响应时间和吞吐量,适用于内存紧张,CPU宽裕的场景。

  • 定期删除:每隔一段时间,程序去过期字典里检查一遍,删除已经过期的键。
    对CUP友好,但是浪费内存(如果在过期后一直不访问,就一直存在,类似内存泄漏)。

  • 惰性删除:不主动删除,就算Key已经过期,也不管。但是每次从字典空间获取,都会检查一遍key是否过期,过期了就删除,未过期,就返回。

RDB持久化

RDB持久化可以手动执行命令(save、bgsave),或者根据配置定期执行,将当前时间的内存数据生成RDB文件,保存到磁盘中,在服务器启动的时候再从磁盘加载RDB文件,来还原内存数据。

SAVE:该命令直接阻塞服务器进程,直到文件创建完毕,在这期间,服务器不能处理任何命令。
BGSAVE:该命令会生成一个子进程,由子进程后台去创建RDB文件,服务器进程继续处理请求。

配置:可以为save设置多个条件,只要有一个条件满足,就会执行BGSAVE命令。配置保存的redisServer的saveParams中。

检查条件:redis会使用serverCron默认每隔100毫秒执行一次,检查是否满足RDB保存条件。

od命令:od -cx dump.rdb 可以打印输出,RDB文件内容。

AOF持久化

AOF是通过记录Redis服务接收的命令,完成持久化操作,在服务器启动时,会加载AOF文件,执行文件中的内容,还原数据库数据。

命令追加:开启AOF命令,有写命令过来会追加到aof_buf缓冲区末尾。

redisServer{

// AOF缓冲区
Sds aof_buf;
}

文件写入:写入函数 flushAppendOnlyFIle()函数,可以根据appendfsync 配置的值,决定写入AOF文件的时机。

  • always: 立即写入
  • everysec :一个线程专门负责执行写入AOF文件,时机 距离上次超过一秒钟。
  • no: 所有内容写入到AOF文件,但是不主动同步到磁盘,由操作系统决定。

执行命令:BGReWriteAOF,Redis服务器会创建一个子进程,维护AOF重写缓冲区,记录所有的写命令。

事件

Redis服务是一个事件驱动程序,分文两种事件:

  • 文件事件:Redis服务端和 客户端,利用套接字通信产生的文件事件。
  • 时间事件:Redis服务端中的一些时间时间,比如 定时函数(serverCron函数)。

文件事件

Redis网络事件处理器是基于Reactor模式实现的。使用I/O多路服务程序同时监听多个套接字,并根据套接字 分发给 不同的事件处理器。

文件事件处理器的组成部分:套接字、I/O多路复用程序、文件事件分发器(dispatcher)、事件处理器组成。
在这里插入图片描述

一次完整的请求
在这里插入图片描述

时间事件

Redis时间事件分为两种:

  • 定时事件:让程序在指定的时间之后执行一次。
  • 周期性事件:让程序 每隔一段时间执行一次。

事件属性:

  • ID:全局唯一ID,并且按顺序递增。
  • when:毫秒精度UNIX时间戳,记录事件的到达时间。
  • timeProc:事件处理器,一个函数。当时间到达时,就调用这个处理器。

实现:
服务器维护了一个无序链表,将所有的时间事件都放进去,每次运行都遍历链表,执行事件到达的 时间处理器。

事件原理:
在这里插入图片描述

客户端

redisServer

redsiServer{
// 一个链表,保存了所有客户端状态
list *clients;
}

redisClient

/* 
 * 因为 I/O 复用的缘故,需要为每个客户端维持一个状态。
 * 多个客户端状态被服务器用链表连接起来。
 */
typedef struct redisClient {

    // 套接字描述符
    int fd;

    // 当前正在使用的数据库
    redisDb *db;

    // 当前正在使用的数据库的 id (号码)
    int dictid;

    // 客户端的名字
    robj *name;             /* As set by CLIENT SETNAME */

    // 查询缓冲区
    sds querybuf;

    // 查询缓冲区长度峰值
    size_t querybuf_peak;   /* Recent (100ms or more) peak of querybuf size */

    // 参数数量
    int argc;

    // 参数对象数组
    robj **argv;

    // 记录被客户端执行的命令
    struct redisCommand *cmd, *lastcmd;

    // 请求的类型:内联命令还是多条命令
    int reqtype;

    // 剩余未读取的命令内容数量
    int multibulklen;       /* number of multi bulk arguments left to read */

    // 命令内容的长度
    long bulklen;           /* length of bulk argument in multi bulk request */

    // 回复链表
    list *reply;

    // 回复链表中对象的总大小
    unsigned long reply_bytes; /* Tot bytes of objects in reply list */

    // 已发送字节,处理 short write 用
    int sentlen;            /* Amount of bytes already sent in the current
                               buffer or object being sent. */

    // 创建客户端的时间
    time_t ctime;           /* Client creation time */

    // 客户端最后一次和服务器互动的时间
    time_t lastinteraction; /* time of the last interaction, used for timeout */

    // 客户端的输出缓冲区超过软性限制的时间
    time_t obuf_soft_limit_reached_time;

    // 客户端状态标志
    int flags;              /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... */

    // 当 server.requirepass 不为 NULL 时
    // 代表认证的状态
    // 0 代表未认证, 1 代表已认证
    int authenticated;      /* when requirepass is non-NULL */

    // 复制状态
    int replstate;          /* replication state if this is a slave */
    // 用于保存主服务器传来的 RDB 文件的文件描述符
    int repldbfd;           /* replication DB file descriptor */

    // 读取主服务器传来的 RDB 文件的偏移量
    off_t repldboff;        /* replication DB file offset */
    // 主服务器传来的 RDB 文件的大小
    off_t repldbsize;       /* replication DB file size */
    
    sds replpreamble;       /* replication DB preamble. */

    // 主服务器的复制偏移量
    long long reploff;      /* replication offset if this is our master */
    // 从服务器最后一次发送 REPLCONF ACK 时的偏移量
    long long repl_ack_off; /* replication ack offset, if this is a slave */
    // 从服务器最后一次发送 REPLCONF ACK 的时间
    long long repl_ack_time;/* replication ack time, if this is a slave */
    // 主服务器的 master run ID
    // 保存在客户端,用于执行部分重同步
    char replrunid[REDIS_RUN_ID_SIZE+1]; /* master run id if this is a master */
    // 从服务器的监听端口号
    int slave_listening_port; /* As configured with: SLAVECONF listening-port */

    // 事务状态
    multiState mstate;      /* MULTI/EXEC state */

    // 阻塞类型
    int btype;              /* Type of blocking op if REDIS_BLOCKED. */
    // 阻塞状态
    blockingState bpop;     /* blocking state */

    // 最后被写入的全局复制偏移量
    long long woff;         /* Last write global replication offset. */

    // 被监视的键
    list *watched_keys;     /* Keys WATCHED for MULTI/EXEC CAS */

    // 这个字典记录了客户端所有订阅的频道
    // 键为频道名字,值为 NULL
    // 也即是,一个频道的集合
    dict *pubsub_channels;  /* channels a client is interested in (SUBSCRIBE) */

    // 链表,包含多个 pubsubPattern 结构
    // 记录了所有订阅频道的客户端的信息
    // 新 pubsubPattern 结构总是被添加到表尾
    list *pubsub_patterns;  /* patterns a client is interested in (SUBSCRIBE) */
    sds peerid;             /* Cached peer ID. */

    /* Response buffer */
    // 回复偏移量
    int bufpos;
    // 回复缓冲区
    char buf[REDIS_REPLY_CHUNK_BYTES];

} redisClient;

未完待续。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值