Redis C/S 交互基础知识「一」|小册免费学

336 阅读2分钟

以往教程都是从 redis 基本数据结构开始,但是在这些数据结构实现前,redis 服务端如何处理客户端命令的流程,以及如何处理这些请求事件:

  • 服务端启动监听
  • 接受命令并解析
  • 执行命令
  • 返回命令请求数据

redis 服务端是典型的事件驱动程序,所以上述说的事件处理尤为重要。redis 将事件分为两大类:文件事件时间时间。文件事件即 socket 读写事件,时间事件用于处理一些周期性执行的定时服务。

为了更好理解服务端和客户端的交互,先看看 redis 数据组织的结构。

robj

redis 是一个 kv 数据库,key 只能是 stringvalue 用结构体 robj 表示,它可以是字符串,列表,集合等,这个取决于 robjtype 字段。

typedef struct redisObject {
    unsigned type:4;		  // 类型(type占4位) -> 5种,定义在<server.h>
    unsigned encoding:4;	  // 编码 -> 11种
    unsigned lru:REDIS_LRU_BITS;  // 对象最后一次被访问的时间
    int refcount;		  // 引用计数
    void *ptr;	                  // 指向实际值的指针
} robj;
  • ptr:为指针,指向实际存储的某一种数据结构
  • refcount:对象的引用次数,实现对象的共享
  • lru:用于实现缓存淘汰策略,可以在 maxmemory_policy 配置最大内存限制的缓存淘汰策略
    • LFU:走 updateLFU(val)
    • LRU:走 LRU_CLOCK(),获取当前时间并更新。存储的是 上次访问时间访问次数

同时针对一个类型的对象, redis 在不同情况下可能采取不同的数据结构存储,这个要看 encoding ,同时在对象的生命周期,encoding 不是一成不变的。

redisDb

robj 是对基础数据结构的封装,另外还需要一个结构体对数据库进行封装,用于管理数据库有关的相关数据以及相关操作,这就是 redisDb

typedef struct redisDb {
    dict *dict;                 // 数据库键空间,保存着数据库中的所有键值对
    dict *expires;              // 键的过期时间,字典的键为键,字典的值为过期事件 UNIX 时间戳
    dict *blocking_keys;        // 正处于阻塞状态的键
    dict *ready_keys;           // 可以解除阻塞的键
    dict *watched_keys;         // 正在被 WATCH 命令监视的键
    int id;                     // 数据库号码
    long long avg_ttl;          // 数据库的键的平均 TTL ,统计信息
    list *defrag_later;         // 尝试逐个碎片整理的key列表
} redisDb;

两个重要的属性:dictexpires

  • scan, move, sort 等是对 dict 键空间的操作;
  • expire, persist 等是对 expires 过期散列表的操作

redisServer

struct redisServer {
    ...
    redisDb *db;		// redis数据库
    int dbnum;		        // db数量,0-16
    dict *commands	        // 所有命令都存储在这个字典中
    ...
    aeEventLoop *el;	        // 事件循环【下篇着重讲这个】
    list *clients;		// 当前连接到服务器的所有客户端
  	...
}

redisServer 的字段实在是太多了,大致就列举这几个】

这里就可以看到 redisServer, redisDb, dict 的关系了:

image.png