Redis-string源码解读

102 阅读4分钟

1、开始之前先看看一个封装命令的结构体

typedef void redisCommandProc(client *c);
typedef int *redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
struct redisCommand {
    char *name;命令的名称
    redisCommandProc *proc;指向命令实际执行的函数的指针
    int arity;参数个数,-N表示大于等于N
    char *sflags;   
    uint64_t flags; 
    redisGetKeysProc *getkeys_proc;
    int firstkey; 
    int lastkey; 
    int keystep;
    long long microseconds, calls;该命令的总的执行时间,和总的调用次数
    int id; 
};

2、下面是string所有的命令

{"get",getCommand,2,"read-only fast @string",0,NULL,1,1,1,0,0,0}
{"set",setCommand,-3,"write use-memory @string",0,NULL,1,1,1,0,0,0}
{"setnx",setnxCommand,3,"write use-memory fast @string",0,NULL,1,1,1,0,0,0}
{"setex",setexCommand,4,"write use-memory @string",0,NULL,1,1,1,0,0,0},
{"psetex",psetexCommand,4,"write use-memory @string",0,NULL,1,1,1,0,0,0},
{"append",appendCommand,3,"write use-memory fast @string",0,NULL,1,1,1,0,0,0},
{"strlen",strlenCommand,2,"read-only fast @string",0,NULL,1,1,1,0,0,0},
{"setrange",setrangeCommand,4,"write use-memory @string",0,NULL,1,1,1,0,0,0},
{"getrange",getrangeCommand,4,"read-only @string",0,NULL,1,1,1,0,0,0},
{"substr",getrangeCommand,4,"read-only @string",0,NULL,1,1,1,0,0,0},
{"incr",incrCommand,2,"write use-memory fast @string",0,NULL,1,1,1,0,0,0},
{"decr",decrCommand,2,"write use-memory fast @string",0,NULL,1,1,1,0,0,0},
{"mget",mgetCommand,-2,"read-only fast @string",0,NULL,1,-1,1,0,0,0},
{"incrby",incrbyCommand,3,"write use-memory fast @string",0,NULL,1,1,1,0,0,0},
{"decrby",decrbyCommand,3,"write use-memory fast @string",0,NULL,1,1,1,0,0,0},
{"incrbyfloat",incrbyfloatCommand,3,"write use-memory fast @string",0,NULL,1,1,1,0,0,0},
{"getset",getsetCommand,3,"write use-memory fast @string",0,NULL,1,1,1,0,0,0},
{"mset",msetCommand,-3,"write use-memory @string",0,NULL,1,-1,2,0,0,0},
{"msetnx",msetnxCommand,-3,"write use-memory @string",0,NULL,1,-1,2,0,0,0},
{"stralgo",stralgoCommand,-2,"read-only @string",0,lcsGetKeys,0,0,0,0,0,0}

3、具体实现

设置字符串
setCommand、setnxCommand、setexCommand、psetexCommand都是由setGenericCommand函数实现
批量设置字符串
msetCommand、msetnxCommand是由msetGenericCommand函数实现
获取字符串
getCommand、getsetCommand是由getGenericCommand函数实现
getrangeCommand
批量获取字符串
mgetCommand
计数器命令
incrCommand、decrCommand、incrbyCommand、decrbyCommand是由incrDecrCommand函数实现
incrbyfloatCommand
修改字符串
appendCommand
setrangeCommand
获取字符串的长度
strlenCommand
其他
stralgoCommand
是不是发现其实string的实现,一共也就是11个函数

4、设置字符串

有哪些参数呢?
key、value、NX、XX、EX、PX、KEEPTTL
对应有几种标志
#define OBJ_SET_NO_FLAGS 0 默认
#define OBJ_SET_NX (1<<0)  setnx、set key value nx  key不存在时设置
#define OBJ_SET_XX (1<<1)  set key value xx  key存在时设置
#define OBJ_SET_EX (1<<2)  
#define OBJ_SET_PX (1<<3)          /* Set if time in ms in given */
#define OBJ_SET_KEEPTTL (1<<4)     /* Set and keep the ttl */
1、解析参数,并赋值
2、通过tryObjectEncoding对value进行编码优化,目的是节省内存
	a、先看编码是不是embstr或raw,不是不做优化,直接返回
    b、如果refcount大于1(共享对象),不做优化,直接返回
    c、根据sds的类型,计算出已经使用的字节数
    d、已经使用的字节数小于等于20,并且字符可以转为数值
    e、服务器没有设置最大内存限制或淘汰策略不是lru(lfu)并且数值范围在0-9999,将
    该robj的引用减一,将共享对象池中的值引用加1,返回共享对象池中的值。
    f、如果robj的encoding是raw,释放原来的指针指向的字符数组,encoding设置为int,
    直接将该值赋给指针ptr。
    g、如果robj的encoding为embstr,将该robj的引用减一,如果服务器没有设置最大内存
    限制或淘汰策略不是lru(lfu)并且数值范围在0-9999,将共享对象池中的值引用加1,返
    回共享对象池中的值;如果值在long的范围内,创建一个robj,encoding为int,ptr为该
    值,否则就创建一个robj,encoding为raw,ptr指向sds的字符数组的地址。
    h、如果字节数小于等于44,并且encoding为embstr,直接返回。
    i、否则,分配一个sizeof(robj)+sizeof(struct sdshdr8)+len+1大小的内存,并初
    始化,将该robj的引用减一,返回该robj。
    j、如果encoding为raw并且sds的可用长度大于已用长度的十分之一,释放多余的空间,结束
3、先看是否设置过期,将过期时间进行转化校验,如果是秒,需要*1000;如果有参数NX并且key
已经存在,回复错误;如果有参数XX并且key不存在,回复错误,结束
4、通过genericSetKey设置key,key不存在dbAdd,存在dbOverwrite,将值的引用加1,如
果设置了keepttl,删除key的过期时间;如果signal为1,发送一个修改key的信号出去
5、如果有,则设置过期时间(当前时间+传入的毫秒值),
6、触发对应事件
7、如果有,触发过期对应的事件
8、回复,结束

5、其他

robj的默认初始化是什么?