jialiguo 发表于 2016-12-20 11:19:00

redis之字符串命令源码解析(三)

形象化设计模式实战             HELLO!架构
 

redis之字符串命令源码解析(一)

redis之字符串命令源码解析(二)
前面已经将set get命令的原理与过程作了详细的解析,下面对一些常用的命令做一些简单讲解,前提是前面的已经明白。

1、append方法解析

 

t_string.c中的appendCommand方法:

void appendCommand(redisClient *c) {
size_t totlen;
robj *o, *append;
// 取出键相应的值对象
o = lookupKeyWrite(c->db,c->argv);
// 如果键值对不存在
if (o == NULL) {
// 键值对不存在,创建一个新的
c->argv = tryObjectEncoding(c->argv);
dbAdd(c->db,c->argv,c->argv);
incrRefCount(c->argv);
//获取字符串对象中字符串值的长度
totlen = stringObjectLen(c->argv);
} else {
// 键值对存在。。。
/* Key exists, check type */
// 检查类型
if (checkType(c,o,REDIS_STRING))
return;
/* "append" is an argument, so always an sds */
// 检查追加操作之后,字符串的长度是否符合 Redis 的限制
append = c->argv;
totlen = stringObjectLen(o)+sdslen(append->ptr);
//检查给定字符串长度 len 是否超过限制值 512 MB
if (checkStringLength(c,totlen) != REDIS_OK)
return;
//解除key的共享,之后就可以进行修改操作(采用的方法是新建一个robj对象,覆盖掉原有的)
        o = dbUnshareStringValue(c->db,c->argv,o);
        //执行追加操作
        o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
        totlen = sdslen(o->ptr);
}
// 向数据库发送键被修改的信号
signalModifiedKey(c->db,c->argv);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"append",c->argv,c->db->id);
// 将服务器设为脏
server.dirty++;
// 发送回复(字符的长度)
addReplyLongLong(c,totlen);
}
大致可图解为:





2、incrby方法解析

void incrbyCommand(redisClient *c) {
long long incr;
//将第二个参数转化成long long类型,指向incr
if (getLongLongFromObjectOrReply(c, c->argv, &incr, NULL) != REDIS_OK) return;
incrDecrCommand(c,incr);
}

void incrDecrCommand(redisClient *c, long long incr) {
long long value, oldvalue;
robj *o, *new;
// 取出值对象
o = lookupKeyWrite(c->db,c->argv);
// 检查对象是否存在,以及类型是否正确
if (o != NULL && checkType(c,o,REDIS_STRING)) return;
// 取出对象的整数值,并保存到 value 参数中
if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
// 检查加法操作执行之后值释放会溢出
// 如果是的话,就向客户端发送一个出错回复,并放弃设置操作
oldvalue = value;
if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
(incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
addReplyError(c,"increment or decrement would overflow");
return;
}
// 进行加法计算,并将值保存到新的值对象中
// 然后用新的值对象替换原来的值对象
value += incr;
new = createStringObjectFromLongLong(value);
if (o)
dbOverwrite(c->db,c->argv,new);
else
dbAdd(c->db,c->argv,new);
// 向数据库发送键被修改的信号
signalModifiedKey(c->db,c->argv);
// 发送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"incrby",c->argv,c->db->id);
// 将服务器设为脏
server.dirty++;
// 返回回复
addReply(c,shared.colon);
addReply(c,new);
addReply(c,shared.crlf);
}

大致可图解为:



 

这里说下“addReply(c,shared.colon);”这行代码,shared.colon是什么?

Redis 在内部使用了一个Flyweight 模式:通过预分配一些常见的值对象,并在多个数据结构之间共享这些对象,程序避免了重复分配的麻烦,也节约了一些CPU时间。redis.c的createSharedObjects创建了shared
shared.colon = createObject(REDIS_STRING,sdsnew(":"));
由可见如果运行incrby test 3,如果test不存在,那么服务器返回的应该是":3\r\n",可以用telnet连接试试。

 

 

总体来看,Redis的结构设计得有许多巧妙之处,为实现字符串各种需求打下了很好的基础,后序还将剖析Redis相比memcache比较特殊的结构。
页: [1]
查看完整版本: redis之字符串命令源码解析(三)