形象化设计模式实战 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[1]); // 如果键值对不存在 if (o == NULL) { // 键值对不存在,创建一个新的 c->argv[2] = tryObjectEncoding(c->argv[2]); dbAdd(c->db,c->argv[1],c->argv[2]); incrRefCount(c->argv[2]); //获取字符串对象中字符串值的长度 totlen = stringObjectLen(c->argv[2]); } else { // 键值对存在。。。 /* Key exists, check type */ // 检查类型 if (checkType(c,o,REDIS_STRING)) return; /* "append" is an argument, so always an sds */ // 检查追加操作之后,字符串的长度是否符合 Redis 的限制 append = c->argv[2]; totlen = stringObjectLen(o)+sdslen(append->ptr); //检查给定字符串长度 len 是否超过限制值 512 MB if (checkStringLength(c,totlen) != REDIS_OK) return; //解除key的共享,之后就可以进行修改操作(采用的方法是新建一个robj对象,覆盖掉原有的) o = dbUnshareStringValue(c->db,c->argv[1],o); //执行追加操作 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr)); totlen = sdslen(o->ptr); } // 向数据库发送键被修改的信号 signalModifiedKey(c->db,c->argv[1]); // 发送事件通知 notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"append",c->argv[1],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[2], &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[1]); // 检查对象是否存在,以及类型是否正确 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[1],new); else dbAdd(c->db,c->argv[1],new); // 向数据库发送键被修改的信号 signalModifiedKey(c->db,c->argv[1]); // 发送事件通知 notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"incrby",c->argv[1],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比较特殊的结构。