之前看了一些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 的槽可以接收这命令。
*/