redis 源码初探

        之前看了一些redis的源码类文章,对于源码有了一些初步的,浅浅的了解。

        从redis的字符串开始入手

         在次之前,要去了解下关于redis的简单动态字符串(SDS),以及sds 优于c语言的,理解为何要这么设计,重复造轮子的原因。

struct sdshdr {
    unsigned int len;  // 字符的长度
    unsigned int free;   // 空余的内存
    char buf[];   //  存储的内容

};

以及对象,reids的string是存放在redisObject的

typedef struct redisObject {
    unsigned type:4;   // 类型
    unsigned encoding:4;   // 编码
    unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */   // 对象最后一次被访问的时间,用于 比如内存不足,删掉离当前最久的key
    int refcount; // 引用计数  好像引用计数小于或者等于0,就被clean
    void *ptr;   // 指向实际值的指针 
} robj;

关于对象类型如下  对象  =>4  指的是如下的

/* Object types */
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4

底下为对象编码

/* Objects encoding. Some kind of objects like Strings and Hashes can be
 * internally represented in multiple ways. The 'encoding' field of the object
 * is set to one of this fields for this object. */
#define REDIS_ENCODING_RAW 0    sds  /* Raw representation */
#define REDIS_ENCODING_INT 1     /* Encoded as integer */
#define REDIS_ENCODING_HT 2      /* Encoded as hash table */
#define REDIS_ENCODING_ZIPMAP 3  /* Encoded as zipmap */
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6  /* Encoded as intset */
#define REDIS_ENCODING_SKIPLIST 7  /* Encoded as skiplist */
#define REDIS_ENCODING_EMBSTR 8   sds /* Embedded sds string encoding */

还有在源码里经常看到  函数变量  redisClient *c  对应的结构体如下,

比如当前客户端的db,或者啥啥啥的。。都是放在这个结构体里  大概注释了一下结构体对应的(红色为get了的)

typedef struct redisClient {
    uint64_t id;         /* Client incremental unique ID. */
    int fd;   // 套接字描述符 
    redisDb *db;    // 当前正在使用的数据库
    int dictid;  //当前正在使用的数据库的 id (号码)
    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 */
    int sentlen;   // 已发送字节,处理 short write 用         /* 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 ... */
    int authenticated;  // 当 server.requirepass 不为 NULL 时  代表认证的状态  0 代表未认证, 1 代表已认证    /* when requirepass is non-NULL */
    int replstate;  // 复制状态        /* replication state if this is a slave */
    int repl_put_online_on_ack; /* Install slave write handler on ACK. */
    int repldbfd;    // 用于保存主服务器传来的 RDB 文件的文件描述符        /* replication DB file descriptor */
    off_t repldboff;  // 读取主服务器传来的 RDB 文件的偏移量       /* replication DB file offset */
    off_t repldbsize;   // 主服务器传来的 RDB 文件的大小    /* replication DB file size */
    sds replpreamble;       /* replication DB preamble. */
    long long reploff;  // 主服务器的复制偏移量     /* replication offset if this is our master */
    long long repl_ack_off;  // 从服务器最后一次发送 REPLCONF ACK 时的偏移量  /* replication ack offset, if this is a slave */
    long long repl_ack_time;  // 从服务器最后一次发送 REPLCONF ACK 的时间 /* replication ack time, if this is a slave */
    char replrunid[REDIS_RUN_ID_SIZE+1]; // 主服务器的 master run ID  保存在客户端,用于执行部分重同步/* 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;

看了下,get出来的数据,不一定都是sds编码 的,需要使用函数 

if (sdsEncodedObject(o))  判断是否为sds编码对象,然后才可以正确的使用sds.c里面的函数

redis源码所有的方法都在redis.c的redisCommandTable,如下例子

{"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
{"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},

最开始看到这些变量的时候,第一反应是百度,现在是根据结构体去跟代码,结构体如下

struct redisCommand {
    char *name;   // 命令名字

     redisCommandProc *proc;   // 实现函数

    int arity;    // 参数个数

    char *sflags;   // 字符串表示的 FLAG /* Flags as string representation, one char per flag. */

    int flags;   // 实际 FLAG   /* The actual flags, obtained from the 'sflags' field. */
    /* Use a function to determine keys arguments in a command line.
     * Used for Redis Cluster redirect. */

    redisGetKeysProc *getkeys_proc;   // 从命令中判断命令的键参数。在 Redis 集群转向时使用。
    /* What keys should be loaded in background when calling this command? */

// 指定哪些参数是 key
    int firstkey; /* The first argument that's a key (0 = no keys) */
    int lastkey;  /* The last argument that's a key */
    int keystep;  /* The step between first and last key */

// 统计信息  microseconds 记录了命令执行耗费的总毫微秒数   calls 是命令被执行的总次数
    long long microseconds, calls;
};

网上搜罗了关于各个参数的详细

* Our command table.

*

* 命令表

*

* Every entry is composed of the following fields:

*

* 表中的每个项都由以下域组成:

*

* name: a string representing the command name.

* 命令的名字

*

* function: pointer to the C function implementing the command.

* 一个指向命令的实现函数的指针

*

* arity: number of arguments, it is possible to use -N to say >= N

* 参数的数量。可以用 -N 表示 >= N

*

* sflags: command flags as string. See below for a table of flags.

* 字符串形式的 FLAG ,用来计算以下的真实 FLAG

*

* flags: flags as bitmask. Computed by Redis using the 'sflags' field.

* 位掩码形式的 FLAG ,根据 sflags 的字符串计算得出

*

* get_keys_proc: an optional function to get key arguments from a command.

* This is only used when the following three fields are not

* enough to specify what arguments are keys.

* 一个可选的函数,用于从命令中取出 key 参数,仅在以下三个参数都不足以表示 key 参数时使用

*

* first_key_index: first argument that is a key

* 第一个 key 参数的位置

*

* last_key_index: last argument that is a key

* 最后一个 key 参数的位置

*

* key_step: step to get all the keys from first to last argument. For instance

* in MSET the step is two since arguments are key,val,key,val,...

* 从 first 参数和 last 参数之间,所有 key 的步数(step)

* 比如说, MSET 命令的格式为 MSET key value [key value ...]

* 它的 step 就为 2

*

* microseconds: microseconds of total execution time for this command.

* 执行这个命令耗费的总微秒数

*

* calls: total number of calls of this command.

* 命令被执行的总次数

*

* The flags, microseconds and calls fields are computed by Redis and should

* always be set to zero.

*

* microseconds 和 call 由 Redis 计算,总是初始化为 0 。

*

* Command flags are expressed using strings where every character represents

* a flag. Later the populateCommandTable() function will take care of

* populating the real 'flags' field using this characters.

*

* 命令的 FLAG 首先由 SFLAG 域设置,之后 populateCommandTable() 函数从 sflags 属性中计算出真正的 FLAG 到 flags 属性中。

*

* This is the meaning of the flags:

*

* 以下是各个 FLAG 的意义:

*

* w: write command (may modify the key space).

* 写入命令,可能会修改 key space

*

* r: read command (will never modify the key space).

* 读命令,不修改 key space

* m: may increase memory usage once called. Don't allow if out of memory.

* 可能会占用大量内存的命令,调用时对内存占用进行检查

*

* a: admin command, like SAVE or SHUTDOWN.

* 管理用途的命令,比如 SAVE 和 SHUTDOWN

*

* p: Pub/Sub related command.

* 发布/订阅相关的命令

*

* f: force replication of this command, regardless of server.dirty.

* 无视 server.dirty ,强制复制这个命令。

*

* s: command not allowed in scripts.

* 不允许在脚本中使用的命令

*

* R: random command. Command is not deterministic, that is, the same command

* with the same arguments, with the same key space, may have different

* results. For instance SPOP and RANDOMKEY are two random commands.

* 随机命令。

* 命令是非确定性的:对于同样的命令,同样的参数,同样的键,结果可能不同。

* 比如 SPOP 和 RANDOMKEY 就是这样的例子。

*

* S: Sort command output array if called from script, so that the output

* is deterministic.

* 如果命令在 Lua 脚本中执行,那么对输出进行排序,从而得出确定性的输出。

*

* l: Allow command while loading the database.

* 允许在载入数据库时使用的命令。

*

* t: Allow command while a slave has stale data but is not allowed to

* server this data. Normally no command is accepted in this condition

* but just a few.

* 允许在附属节点带有过期数据时执行的命令。

* 这类命令很少有,只有几个。

*

* M: Do not automatically propagate the command on MONITOR.

* 不要在 MONITOR 模式下自动广播的命令。

*

* k: Perform an implicit ASKING for this command, so the command will be

* accepted in cluster mode if the slot is marked as 'importing'.

* 为这个命令执行一个显式的 ASKING ,

* 使得在集群模式下,一个被标示为 importing 的槽可以接收这命令。

*/

         

    

猜你喜欢

转载自blog.csdn.net/weixin_36224759/article/details/81632432