redis原理-对象以及命令解析与执行

redis原理-对象以及命令解析与执行

一、  redis对象

适用场景:

redis是使用对象来存放数据的,基于前面的几种数据结构创建对象。

原理解释:

redis对象类型有下面5种:

   /*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 objectslike Strings and Hashes can be

 *internally represented in multiple ways. The 'encoding' field of the object

 * isset to one of this fields for this object. */

#define REDIS_ENCODING_RAW 0     /* Raw representation */  sds字符串

#define REDIS_ENCODING_INT 1     /* Encoded as integer */  整形

#define REDIS_ENCODING_HT 2      /* Encoded as hash table */ hash表

#define REDIS_ENCODING_ZIPMAP 3  /* Encoded as zipmap */ 

#define REDIS_ENCODING_LINKEDLIST 4 /*Encoded as regular linked list */ 双端列表

#define REDIS_ENCODING_ZIPLIST 5 /* Encodedas ziplist */    压缩列表

#define REDIS_ENCODING_INTSET 6  /* Encoded as intset */      整数集合

#define REDIS_ENCODING_SKIPLIST 7  /* Encoded as skiplist */   跳跃表

#define REDIS_ENCODING_EMBSTR 8  /* Embedded sds string encoding */ 基于emb格式的sds

 

对象对应的编码:

REDIS_STRING:REDIS_ENCODING_RAW, REDIS_ENCODING_INT, REDIS_ENCODING_EMBSTR

REDIS_LIST:REDIS_ENCODING_LINKEDLIST,REDIS_ENCODING_ZIPLIST

REDIS_SET:REDIS_ENCODING_INTSET,REDIS_ENCODING_HT

REDIS_ZSET:REDIS_ENCODING_ZIPLIST,REDIS_ENCODING_SKIPLIST

REDIS_HASH:REDIS_ENCODING_ZIPLIST,REDIS_ENCODING_HT

 

//编码对应的字符串

char *strEncoding(int encoding) {

   switch(encoding) {

   case REDIS_ENCODING_RAW: return "raw";

   case REDIS_ENCODING_INT: return "int";

   case REDIS_ENCODING_HT: return "hashtable";

   case REDIS_ENCODING_LINKEDLIST: return "linkedlist";

   case REDIS_ENCODING_ZIPLIST: return "ziplist";

   case REDIS_ENCODING_INTSET: return "intset";

    caseREDIS_ENCODING_SKIPLIST: return "skiplist";

   case REDIS_ENCODING_EMBSTR: return "embstr";

   default: return "unknown";

   }  

}

 

//redis对象

typedef struct redisObject {

   unsigned type:4;   //类型

   unsigned encoding:4;   //编码

   unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock)

   int refcount;//引用个数

 

   void *ptr; //实际数据

} robj;

 

创建对象:

robj *createObject(int type, void *ptr) {

   robj *o = zmalloc(sizeof(*o));

   o->type = type;

   o->encoding = REDIS_ENCODING_RAW;

   o->ptr = ptr;

   o->refcount = 1;

   /* Set the LRU to the current lruclock (minutes resolution). */

   o->lru = LRU_CLOCK();

   return o;

}

 

//创建一个sds字符串,这里分配了2次空间 sdsnewlen 和 createObject:zmalloc

/* Create a string object with encodingREDIS_ENCODING_RAW, that is a plain

 *string object where o->ptr points to a proper sds string. */

robj *createRawStringObject(char *ptr,size_t len) {

   returncreateObject(REDIS_STRING,sdsnewlen(ptr,len));

}

 

//创建一个emb格式的字符串,分配了一次空间

/* Create a string object with encodingREDIS_ENCODING_EMBSTR, that is

* an object where the sds string isactually an unmodifiable string

* allocated in the same chunk as the objectitself. */

robj *createEmbeddedStringObject(char *ptr,size_t len) {

robj *o = zmalloc(sizeof(robj)+sizeof(structsdshdr)+len+1);

struct sdshdr *sh = (void*)(o+1);

 

o->type = REDIS_STRING;

o->encoding = REDIS_ENCODING_EMBSTR;

o->ptr = sh+1;

o->refcount = 1;

o->lru = LRU_CLOCK();

 

sh->len = len;

sh->free = 0;

if (ptr) {

memcpy(sh->buf,ptr,len);

sh->buf[len] = '\0';

} else {

memset(sh->buf,0,len+1);

}

return o;

}

 

//如果长度小于39咱们就创建emb字符串,否则就原始的sds

/* Create a string object with EMBSTRencoding if it is smaller than

 *REIDS_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is

 *used.

 *

 *The current limit of 39 is chosen so that the biggest string object

 * weallocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */

#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 39

robj *createStringObject(char *ptr, size_tlen) {

   if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT)

       return createEmbeddedStringObject(ptr,len);

   else

       return createRawStringObject(ptr,len);

}

//longlong类型也可以存储为字符串

robj *createStringObjectFromLongLong(longlong value) {

robj*o;

//首先从引用里面找

   if (value >= 0 && value < REDIS_SHARED_INTEGERS) {

       incrRefCount(shared.integers[value]);

       o = shared.integers[value];

    }else {

       if (value >= LONG_MIN && value <= LONG_MAX) {

           o = createObject(REDIS_STRING, NULL);

           o->encoding = REDIS_ENCODING_INT;

           o->ptr = (void*)((long)value);//直接转换成指针

       } else {

           o = createObject(REDIS_STRING,sdsfromlonglong(value));

       }  

   }  

   return o;

}

 

//获取longlong对象

int getLongLongFromObject(robj *o, longlong *target) {

   long long value;

   char *eptr;

 

   if (o == NULL) {

       value = 0;

    }else {

       redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);

       if (sdsEncodedObject(o)) {

           errno = 0;

           value = strtoll(o->ptr, &eptr, 10);

           if (isspace(((char*)o->ptr)[0]) || eptr[0] != '\0' ||

                errno == ERANGE)

                return REDIS_ERR;

       } else if (o->encoding == REDIS_ENCODING_INT) {

           value = (long)o->ptr;//转换成值

       } else {

           redisPanic("Unknown string encoding");

       }

    }

   if (target) *target = value;

   return REDIS_OK;

}

//创建列表对象

robj *createListObject(void) {

   list *l = listCreate();

   robj *o = createObject(REDIS_LIST,l);

   listSetFreeMethod(l,decrRefCountVoid);

   o->encoding = REDIS_ENCODING_LINKEDLIST;

   return o;

}

//创建压缩列表对象

robj *createZiplistObject(void) {

   unsigned char *zl = ziplistNew();

   robj *o = createObject(REDIS_LIST,zl);

   o->encoding = REDIS_ENCODING_ZIPLIST;

   return o;

}

 

//创建集合对象

robj *createSetObject(void) {

   dict *d = dictCreate(&setDictType,NULL);

   robj *o = createObject(REDIS_SET,d);

   o->encoding = REDIS_ENCODING_HT;

   return o;

}

//创建整数集合对象

robj *createIntsetObject(void) {

    intset *is = intsetNew();

   robj *o = createObject(REDIS_SET,is);

   o->encoding = REDIS_ENCODING_INTSET;

   return o;

}

//创建hash对象

robj *createHashObject(void) {

   unsigned char *zl = ziplistNew();

   robj *o = createObject(REDIS_HASH, zl);

   o->encoding = REDIS_ENCODING_ZIPLIST;

   return o;

}

//创建有序集合对象

robj *createZsetObject(void) {

   zset *zs = zmalloc(sizeof(*zs));

   robj *o;

 

   zs->dict = dictCreate(&zsetDictType,NULL);

   zs->zsl = zslCreate();

    o= createObject(REDIS_ZSET,zs);

   o->encoding = REDIS_ENCODING_SKIPLIST;

   return o;

}

 

//关于编码转换的一些说明:

列表对象:都满足下面2个条件就会使用ziplist,否则使用双端链表:

1、 所有字符串长度都小于64

2、 保存的元素个数小于512

 

hash对象:都满足下面2个条件就会使用ziplist,否则使用hash表:

1、 hash对象保存的键值对字符长度都小于64

2、 键值对对数数量小于512

 

集合对象:都满足下面2个条件使用intset,否则使用hash表:

1、 集合的所有元素都是整数

2、 集合数量不能超过512

 

有序集合:都满足下面2个条件使用ziplist,否则使用跳跃表:

1、 有序集合元素小于128个

2、 有序集合元素成员长度都小于64字节

 

 

二、  命令解析与执行

//针对不同命令,redis使用redisCommand这个结构体统一实现命令的

//命令实现回调函数

typedef void redisCommandProc(redisClient*c);

typedef int *redisGetKeysProc(structredisCommand *cmd, robj **argv, int argc, int *numkeys);

 

//命令结构体定义

struct redisCommand {

   char *name; //命令的名字

   redisCommandProc *proc;//实际处理命令的函数

   int arity; //参数个数

   char *sflags; /* Flags as string representation, one char per flag. */字符串flags(标志用户可以执行什么操作)

   int flags;    /* 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;

   /* What keys should be loaded in background when calling this command?*/

   int firstkey; /* The first argument that's a key (0 = no keys) */  //指示那个参数是key

   int lastkey;  /* The last argumentthat's a key */

   int keystep;  /* The step betweenfirst and last key */

   long long microseconds, calls;//命令执行的毫秒数,执行的总次数

};

 

//命令列表部分展示

struct redisCommand redisCommandTable[] = {

   {"get",getCommand,2,"r",0,NULL,1,1,1,0,0},

   {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},

   {"setnx",setnxCommand,3,"wm",0,NULL,1,1,1,0,0},

   {"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},

   {"psetex",psetexCommand,4,"wm",0,NULL,1,1,1,0,0},

   {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0},

   {"strlen",strlenCommand,2,"r",0,NULL,1,1,1,0,0},

   {"del",delCommand,-2,"w",0,NULL,1,-1,1,0,0},

   {"exists",existsCommand,2,"r",0,NULL,1,1,1,0,0},

   {"setbit",setbitCommand,4,"wm",0,NULL,1,1,1,0,0},

   {"getbit",getbitCommand,3,"r",0,NULL,1,1,1,0,0},

   {"setrange",setrangeCommand,4,"wm",0,NULL,1,1,1,0,0},

   {"getrange",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},

   {"substr",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},

}

命令说明:

/* 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

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

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

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

 *                This is only used when thefollowing three fields are not

 *                enough to specify whatarguments are keys.

 *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//key的步长

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

 *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 //由redis通过sflags计算

 * alwaysbe set to zero.//默认初始化为0

 *

 *Command flags are expressed using strings where every character represents

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

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

 *

 *This is the meaning of the flags://标志代表的意思

 *

 * w:write command (may modify the key space). //写入命令 可能会修改keyspace

 * r:read command  (will never modify the keyspace). //读命令,永远不会修改keyspace

 * m:may increase memory usage once called. Don't allow if out of memory.//执行时可能会占用很大的内存,执行的时候需要检测

 * a:admin command, like SAVE or 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.

 * S:Sort command output array if called from script, so that the output//如果在脚本中对输出的排序,结果是确定性的

 *    isdeterministic.

 * 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

 *    butjust 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'.

 * F:Fast command: O(1) or O(log(N)) command that should never delay //时间复杂度很少的命令

 *    itsexecution as long as the kernel scheduler is giving us time.

 *   Note that commands that may trigger a DEL as a side effect (like SET)

 *    arenot fast commands.

 

 

 

在redis.h文件定义了一个全局的 redisServer的服务器类,所有的管理都在这个类里面。

 

redisServer:

   dict *commands;             /*Command table */ 命令列表

   dict *orig_commands;        /*Command table before command renaming. */ 命令重命名前列表(命令备份列表)

 

//把命令映射到redisServer的commands和orig_commands里面。

/* Populates the Redis Command Tablestarting from the hard coded list

 * wehave on top of redis.c file. */

void populateCommandTable(void) {

   int j;

   int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);//命令个数

 

   for (j = 0; j < numcommands; j++) {

       struct redisCommand *c = redisCommandTable+j;//根据索引获取到当前命令

       char *f = c->sflags;

       int retval1, retval2;

 

        //这里根据设置设置标志

       while(*f != '\0') {

           switch(*f) {

           case 'w': c->flags |= REDIS_CMD_WRITE; break;

           case 'r': c->flags |= REDIS_CMD_READONLY; break;

           case 'm': c->flags |= REDIS_CMD_DENYOOM; break;

           case 'a': c->flags |= REDIS_CMD_ADMIN; break;

           case 'p': c->flags |= REDIS_CMD_PUBSUB; break;

           case 's': c->flags |= REDIS_CMD_NOSCRIPT; break;

           case 'R': c->flags |= REDIS_CMD_RANDOM; break;

           case 'S': c->flags |= REDIS_CMD_SORT_FOR_SCRIPT; break;

           case 'l': c->flags |= REDIS_CMD_LOADING; break;

           case 't': c->flags |= REDIS_CMD_STALE; break;

           case 'M': c->flags |= REDIS_CMD_SKIP_MONITOR; break;

           case 'k': c->flags |= REDIS_CMD_ASKING; break;

           case 'F': c->flags |= REDIS_CMD_FAST; break;

           default: redisPanic("Unsupported command flag"); break;

           }   

           f++;

       }   

        //添加到字典中

       retval1 = dictAdd(server.commands, sdsnew(c->name), c);

       /* Populate an additional dictionary that will be unaffected

        * by rename-command statements in redis.conf. */

//在备份字典中也添加上

       retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);

        //断言测试

       redisAssert(retval1 == DICT_OK && retval2 == DICT_OK);

   }   

}

 

//字符所对应的标志

/* Command flags. Please check the commandtable defined in the redis.c file

 *for more information about the meaning of every flag. */

#define REDIS_CMD_WRITE 1                   /* "w" flag */

#define REDIS_CMD_READONLY 2                /* "r" flag */

#define REDIS_CMD_DENYOOM 4                 /* "m" flag */

#define REDIS_CMD_NOT_USED_1 8              /* no longer used flag */

#define REDIS_CMD_ADMIN 16                  /* "a" flag */

#define REDIS_CMD_PUBSUB 32                 /* "p" flag */

#define REDIS_CMD_NOSCRIPT  64              /* "s" flag */

#define REDIS_CMD_RANDOM 128                /* "R" flag */

#define REDIS_CMD_SORT_FOR_SCRIPT 256       /* "S" flag */

#define REDIS_CMD_LOADING 512               /* "l" flag */

#define REDIS_CMD_STALE 1024                /* "t" flag */

#define REDIS_CMD_SKIP_MONITOR 2048         /* "M" flag */

#define REDIS_CMD_ASKING 4096               /* "k" flag */

 

 

 

在redis.h文件定义了一个全局的 redisClient的客户端类。

 

redisClient:

   robj *name;             /* As setby CLIENT SETNAME */

   sds querybuf;

   int argc;         //参数个数

   robj **argv;       //具体参数

   struct redisCommand *cmd, *lastcmd; //关联的命令

 

 

命令解析流程:

1、redis.c/initServer->2、 redis.c/aeCreateFileEvent ->  3、networking.c/acceptTcpHandler  -> networking.c/ 4、acceptCommonHandler-> 5、 networking.c/ createClient  -> 6、ae.c/ aeCreateFileEvent ->7、networking.c/ readQueryFromClient

 

//接收数据的函数

void readQueryFromClient(aeEventLoop *el,int fd, void *privdata, int mask) {

   redisClient *c = (redisClient*) privdata;

   int nread, readlen;

   size_t qblen;

   REDIS_NOTUSED(el);

   REDIS_NOTUSED(mask);

 

   server.current_client = c; //设置服务器当前的客户端

   readlen = REDIS_IOBUF_LEN;  //#defineREDIS_IOBUF_LEN   (1024*16)  /* Generic I/O buffer size */

   /* If this is a multi bulk request, and we are processing a bulk reply

    * that is large enough, try to maximize the probability that the query

    * buffer contains exactly the SDS string representing the object, even

    * at the risk of requiring more read(2) calls. This way the function

    * processMultiBulkBuffer() can avoid copying buffers to create the

    * Redis Object representing the argument. */

   if (c->reqtype == REDIS_REQ_MULTIBULK && c->multibulklen&& c->bulklen != -1

       && c->bulklen >= REDIS_MBULK_BIG_ARG)

   {   

       int remaining = (unsigned)(c->bulklen+2)-sdslen(c->querybuf);

 

       if (remaining < readlen) readlen = remaining;

   }   

 

   qblen = sdslen(c->querybuf);

   if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;

c->querybuf= sdsMakeRoomFor(c->querybuf, readlen);

 

//把数据读入自身的缓冲区

   nread = read(fd, c->querybuf+qblen, readlen);

   if (nread == -1) {

       if (errno == EAGAIN) {

           nread = 0;

       } else {

           redisLog(REDIS_VERBOSE, "Reading from client:%s",strerror(errno));

           freeClient(c);

           return;

       }   

    }else if (nread == 0) {

       redisLog(REDIS_VERBOSE, "Client closed connection");

       freeClient(c);

       return;

    }

   if (nread) {

       sdsIncrLen(c->querybuf,nread);

        c->lastinteraction = server.unixtime;

       if (c->flags & REDIS_MASTER) c->reploff += nread;

       server.stat_net_input_bytes += nread;

    }else {

       server.current_client = NULL;

       return;

}

//最大的请求数据长度是1g

//server.client_max_querybuf_len = #define REDIS_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max //query buffer. */

   if (sdslen(c->querybuf) > server.client_max_querybuf_len) {

       sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty();

 

       bytes = sdscatrepr(bytes,c->querybuf,64);

      redisLog(REDIS_WARNING,"Closing client that reached max querybuffer length: %s (qbuf initial bytes: %s)", ci, bytes);

       sdsfree(ci);

       sdsfree(bytes);

       freeClient(c);

       return;

}

//处理输入的缓冲区数据

   processInputBuffer(c);

   server.current_client = NULL;

}

 

//处理缓冲区数据

void processInputBuffer(redisClient *c) {

/*Keep processing while there is something in the input buffer */

//一直当缓冲区数据为空

   while(sdslen(c->querybuf)) {

       /* Return if clients are paused. */

       if (!(c->flags & REDIS_SLAVE) && clientsArePaused())return;

 

       /* Immediately abort if the client is in the middle of something. */

       if (c->flags & REDIS_BLOCKED) return;

 

       /* REDIS_CLOSE_AFTER_REPLY closes the connection once the reply is

        * written to the client. Make sure to not let the reply grow after

        * this flag has been set (i.e. don't process more commands). */

       if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;

 

       /* Determine request type when unknown. */

//确定未知的请求类型

       if (!c->reqtype) {

            //如果请求的数据第一位是*代表是多条

           if (c->querybuf[0] == '*') {

                c->reqtype =REDIS_REQ_MULTIBULK;

           } else {

            //内联命令

                c->reqtype =REDIS_REQ_INLINE;

           }   

       }   

    //官方关于内联和多条的解释

//Multiple commands andpipelining

//Aclient can use the same connection in order to issue multiple commands.Pipelining is supported //so multiple commands can be sent with a single writeoperation by the client, without the need //to read the server reply of theprevious command before issuing the next one. All the replies can //be read atthe end.

//Formore information please check our page about Pipelining.

//一个客户端可以用同一个连接发送多个命令顺序执行,顺序执行支持多个命令,客户端不用等到前一个命令//回复,所有的操作可以在最后返回

//Inline Commands

//Sometimesyou have only telnet in your hands and you need to send a command tothe Redis server. //While the Redis protocol is simple to implement it is notideal to use in interactive sessions, //and redis-cli may not alwaysbe available. For this reason Redis also accepts commands in a //special waythat is designed for humans, and is called theinline command format.

//Thefollowing is an example of a server/client chat using an inline command (theserver chat starts //with S:, the client chat with C:)

//C:PING

//S:+PONG

//Thefollowing is another example of an inline command returning an integer:

//C:EXISTS somekey

//S::0

//有时候你可能会用类似telnet客户端给redis服务器发送命令,客户端可能有时候不能用,基于这种原因redis服务器支持这种交互的内联命令

 

       if (c->reqtype == REDIS_REQ_INLINE) {

            //处理内联命令

           if (processInlineBuffer(c) != REDIS_OK) break;

       } else if (c->reqtype == REDIS_REQ_MULTIBULK) {

            //处理多个命令

           if (processMultibulkBuffer(c) != REDIS_OK) break;

       } else {

           redisPanic("Unknown request type");

       }   

       

       /* Multibulk processing could see a <= 0 length. */

       if (c->argc == 0) {

           resetClient(c);

       } else {

           /* Only reset the client when the command was executed. */

            //处理命令

           if (processCommand(c) == REDIS_OK)

                resetClient(c);

       }   

    }

}

 

//处理内联命令

int processInlineBuffer(redisClient *c) {

   char *newline;

   int argc, j;

   sds *argv, aux;

   size_t querylen;

 

/*Search for end of line */

//搜索到第一个\n结束

   newline = strchr(c->querybuf,'\n');

 

   /* Nothing to do without a \r\n */

   if (newline == NULL) {

       if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {

           addReplyError(c,"Protocol error: too big inline request");

           setProtocolError(c,0);

       }

       return REDIS_ERR;

    }

 

   /* Handle the \r\n case. */

   if (newline && newline != c->querybuf && *(newline-1)== '\r')

       newline--;

 

/*Split the input buffer up to the \r\n */

   querylen = newline-(c->querybuf);

aux =sdsnewlen(c->querybuf,querylen);

 

//分割数据到argv

   argv = sdssplitargs(aux,&argc);

   sdsfree(aux);

   if (argv == NULL) {

       addReplyError(c,"Protocol error: unbalanced quotes inrequest");

       setProtocolError(c,0);

       return REDIS_ERR;

    }

 

   /* Newline from slaves can be used to refresh the last ACK time.

    * This is useful for a slave to ping back while loading a big

    * RDB file. */

   if (querylen == 0 && c->flags & REDIS_SLAVE)

       c->repl_ack_time = server.unixtime;

 

/*Leave data after the first line of the query in the buffer */

//删除已经读的

   sdsrange(c->querybuf,querylen+2,-1);

 

//对每一个参数分配一个字符串对象,并且赋值

/*Setup argv array on client structure */

//如果个数大于0,咱就是否以前有的数据,然后重新分配空间

   if (argc) {

       if (c->argv) zfree(c->argv);

       c->argv = zmalloc(sizeof(robj*)*argc);

    }

   /* Create redis objects for all arguments. */

   for (c->argc = 0, j = 0; j < argc; j++) {

       if (sdslen(argv[j])) {

            //保存数据都argv

           c->argv[c->argc] = createObject(REDIS_STRING,argv[j]);

            c->argc++;

       } else {

           sdsfree(argv[j]);

       }

}

//把临时的释放了

   zfree(argv);

   return REDIS_OK;

}

 

//处理多条的

int processMultibulkBuffer(redisClient *c){

   char *newline = NULL;

   int pos = 0, ok;

   long long ll;

 

   if (c->multibulklen == 0) {

       /* The client should have been reset */

       redisAssertWithInfo(c,NULL,c->argc == 0);

        //*3\r\n

//$3\r\n

//SET\r\n

//$5\r\n

//HENRY\r\n

//$8\r\n

//HENRYFAN\r\n

 

       /* Multi bulk length cannot be read without a \r\n */

    //如果找不到\r 说明协议是有问题的

       newline = strchr(c->querybuf,'\r');

       if (newline == NULL) {

        //如果长度大于#define REDIS_INLINE_MAX_SIZE  (1024*64) /* Max size of inline reads */ 64k

           if (sdslen(c->querybuf) > REDIS_INLINE_MAX_SIZE) {

                addReplyError(c,"Protocolerror: too big mbulk count string");

                setProtocolError(c,0);

           }   

           return REDIS_ERR;

       }   

       

        //判断buff的长度是否符合规范

       /* Buffer should also contain \n */

       if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))

           return REDIS_ERR;

 

       /* We know for sure there is a whole line since newline != NULL,

        * so go ahead and find out the multi bulk length. */

        //断言此处第一个字符是 *

       redisAssertWithInfo(c,NULL,c->querybuf[0] == '*');

        //获取到 第一个字符串并且转换成一个长整形

       ok = string2ll(c->querybuf+1,newline-(c->querybuf+1),&ll);

        //如果转换的字符串为空或者大于1m

        if (!ok || ll > 1024*1024) {

           addReplyError(c,"Protocol error: invalid multibulk length");

           setProtocolError(c,pos);

           return REDIS_ERR;

       }   

       

        //定位新的位置

       pos = (newline-c->querybuf)+2;

       if (ll <= 0) {

           sdsrange(c->querybuf,pos,-1);

           return REDIS_OK;

       }

       

        //这里是数据的个数

       c->multibulklen = ll;

       

        //释放以前的

       /* Setup argv array on client structure */

       if (c->argv) zfree(c->argv);

       //按数据个数来分配缓冲区

c->argv= zmalloc(sizeof(robj*)*c->multibulklen);

    }

   

   redisAssertWithInfo(c,NULL,c->multibulklen > 0);

   while(c->multibulklen) {

       /* Read bulk length if unknown */

       if (c->bulklen == -1) {

           newline = strchr(c->querybuf+pos,'\r');

           if (newline == NULL) {

                if (sdslen(c->querybuf) >REDIS_INLINE_MAX_SIZE) {

                    addReplyError(c,

                        "Protocol error:too big bulk count string");

                    setProtocolError(c,0);

                    return REDIS_ERR;

                }

                break;

           }

           

            //检测数据正确性

           /* Buffer should also contain \n */

           if (newline-(c->querybuf) > ((signed)sdslen(c->querybuf)-2))

               break;

 

            //长度必须以 $开头

           if (c->querybuf[pos] != '$') {

                addReplyErrorFormat(c,

                    "Protocol error:expected '$', got '%c'",

                    c->querybuf[pos]);

                setProtocolError(c,pos);

                return REDIS_ERR;

           }

           

            //截取参数长度

           ok =string2ll(c->querybuf+pos+1,newline-(c->querybuf+pos+1),&ll);

           if (!ok || ll < 0 || ll > 512*1024*1024) {

                addReplyError(c,"Protocolerror: invalid bulk length");

                setProtocolError(c,pos);

                return REDIS_ERR;

           }

 

           pos += newline-(c->querybuf+pos)+2;

           if (ll >= REDIS_MBULK_BIG_ARG) {

                size_t qblen;

 

                /* If we are going to read alarge object from network

                 * try to make it likely thatit will start at c->querybuf

                 * boundary so that we canoptimize object creation

                * avoiding a large copy ofdata. */

               sdsrange(c->querybuf,pos,-1);

                pos = 0;

                qblen = sdslen(c->querybuf);

                /* Hint the sds library aboutthe amount of bytes this string is

                 * going to contain. */

                if (qblen < (size_t)ll+2)

                    c->querybuf =sdsMakeRoomFor(c->querybuf,ll+2-qblen);

           }

            //设置参数长度

           c->bulklen = ll;

       }

 

        //开始截取参数

       /* Read bulk argument */

       if (sdslen(c->querybuf)-pos < (unsigned)(c->bulklen+2)) {

           /* Not enough data (+2 == trailing \r\n) */

           break;

       } else {

           /* Optimization: if the buffer contains JUST our bulk element

            * instead of creating a new object by *copying* the sds we

            * just use the current sds string. */

           if (pos == 0 &&

                c->bulklen >=REDIS_MBULK_BIG_ARG &&

                (signed) sdslen(c->querybuf)== c->bulklen+2)

           {

                c->argv[c->argc++] =createObject(REDIS_STRING,c->querybuf);

                sdsIncrLen(c->querybuf,-2);/* remove CRLF */

                c->querybuf = sdsempty();

                /* Assume that if we saw a fatargument we'll see another one

                 * likely... */

                c->querybuf =sdsMakeRoomFor(c->querybuf,c->bulklen+2);

                pos = 0;

           } else {

 

                //保存数据到argv

                c->argv[c->argc++] =

                   createStringObject(c->querybuf+pos,c->bulklen);

                pos += c->bulklen+2;

           }

           c->bulklen = -1;

           c->multibulklen--;

       }

    }

 

   /* Trim to pos */

   if (pos) sdsrange(c->querybuf,pos,-1);

 

   /* We're done when c->multibulk == 0 */

   if (c->multibulklen == 0) return REDIS_OK;

 

   /* Still not read to process the command */

   return REDIS_ERR;

}

 

 

 

//处理和过滤命令

/* If this function gets called we alreadyread a whole

 *command, arguments are in the client argv/argc fields.

 *processCommand() execute the command or prepare the

 *server for a bulk read from the client.

 *

 * If1 is returned the client is still alive and valid and

 *other operations can be performed by the caller. Otherwise

 * if0 is returned the client was destroyed (i.e. after QUIT). */

//在这个函数,我们已经获取到了一个完整的命令,参数等信息保存在argv/argc字段里面,

//返回1标示客户端仍然存活,caller可以继续执行其他命令,返回0标示客户端已经销毁

int processCommand(redisClient *c) {

   /* The QUIT command is handled separately. Normal command procs will

    * go through checking for replication and QUIT will cause trouble

    * when FORCE_REPLICATION is enabled and would be implemented in

    * a regular command proc. */

    //是退出命令就直接处理了

   if (!strcasecmp(c->argv[0]->ptr,"quit")) {

       addReply(c,shared.ok);

       c->flags |= REDIS_CLOSE_AFTER_REPLY;

       return REDIS_ERR;

   }   

 

   /* Now lookup the command and check ASAP about trivial error conditions

    * such as wrong arity, bad command name and so forth. */

    //在集合里面查找命令

c->cmd= c->lastcmd = lookupCommand(c->argv[0]->ptr);

//命令没有找到

if(!c->cmd) {

        //如果是事务,就设置事务标志

       flagTransaction(c);

        //回复 未知命令

       addReplyErrorFormat(c,"unknown command '%s'",

           (char*)c->argv[0]->ptr);

       return REDIS_OK;

    }else if ((c->cmd->arity > 0 && c->cmd->arity !=c->argc) ||//错误的参数个数

               (c->argc <-c->cmd->arity)) {

       flagTransaction(c);

       addReplyErrorFormat(c,"wrong number of arguments for '%s'command",

           c->cmd->name);

       return REDIS_OK;

   }   

 

 

    //检测是否授权

   /* Check if the user is authenticated */

   if (server.requirepass && !c->authenticated &&c->cmd->proc != authCommand)

    {

       flagTransaction(c);

       addReply(c,shared.noautherr);

       return REDIS_OK;

    }

 

    //集群模式处理

   /* If cluster is enabled perform the cluster redirection here.

    * However we don't perform the redirection if: //我们不进行转向操作

    * 1) The sender of this command is our master.//发送命令是主节点

    * 2) The command has no key arguments. */  //命令没有参数

   if (server.cluster_enabled &&

       !(c->flags & REDIS_MASTER) &&

       !(c->flags & REDIS_LUA_CLIENT &&

         server.lua_caller->flags & REDIS_MASTER) &&

       !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey== 0))

{       

inthashslot;

       if (server.cluster->state != REDIS_CLUSTER_OK) {

           flagTransaction(c);

           clusterRedirectClient(c,NULL,0,REDIS_CLUSTER_REDIR_DOWN_STATE);

           return REDIS_OK;

       } else {

           int error_code;

           clusterNode *n =getNodeByQuery(c,c->cmd,c->argv,c->argc,&hashslot,&error_code);

           if (n == NULL || n != server.cluster->myself) {

                flagTransaction(c);

                clusterRedirectClient(c,n,hashslot,error_code);

                return REDIS_OK;

           }

       }

    }

 

 

    //如果设置了最大内存

   /* Handle the maxmemory directive.

    *

    * First we try to free some memory if possible (if there are volatile

    * keys in the dataset). If there are not the only thing we can do

    * is returning an error. */

    //首先我们尽可能尝试是否一些内存,

if(server.maxmemory) {

       int retval = freeMemoryIfNeeded();

       /* freeMemoryIfNeeded may flush slave output buffers. This may result

        * into a slave, that may be the active client, to be freed. */

       if (server.current_client == NULL) return REDIS_ERR;

 

       /* It was impossible to free enough memory, and the command the client

        * is trying to execute is denied during OOM conditions? Error. */

        //如果命令会占用很大的内存,咱就返回oom了

       if ((c->cmd->flags & REDIS_CMD_DENYOOM) && retval ==REDIS_ERR) {

           flagTransaction(c);

           addReply(c, shared.oomerr);

           return REDIS_OK;

       }

    }

   

    //如果是主服务器,并且服务器之前执行了bgsave发生了错误,咱就返回

   /* Don't accept write commands if there are problems persisting on disk

    * and if this is a master instance. */

   if (((server.stop_writes_on_bgsave_err &&

         server.saveparamslen > 0 &&

         server.lastbgsave_status == REDIS_ERR) ||

         server.aof_last_write_status == REDIS_ERR) &&

       server.masterhost == NULL &&

       (c->cmd->flags & REDIS_CMD_WRITE ||

        c->cmd->proc == pingCommand))

    {

       flagTransaction(c);

       if (server.aof_last_write_status == REDIS_OK)

           addReply(c, shared.bgsaveerr);

       else

           addReplySds(c,

                sdscatprintf(sdsempty(),"-MISCONFErrors writing to the AOF file: %s\r\n",

               strerror(server.aof_last_write_errno)));

       return REDIS_OK;

    }

   

// min-slaves-to-write已经打开,如果没有good 从服务器,拒绝执行写命令

   /* Don't accept write commands if there are not enough good slaves and

    * user configured the min-slaves-to-write option. */

   if (server.masterhost == NULL &&

       server.repl_min_slaves_to_write &&

       server.repl_min_slaves_max_lag &&

       c->cmd->flags & REDIS_CMD_WRITE &&

       server.repl_good_slaves_count < server.repl_min_slaves_to_write)

    {

       flagTransaction(c);

       addReply(c, shared.noreplicaserr);

       return REDIS_OK;

    }

 

    //如果只有一个制度的从服务器,拒绝执行写命令

   /* Don't accept write commands if this is a read only slave. But

    * accept write commands if this is our master. */

   if (server.masterhost && server.repl_slave_ro &&

       !(c->flags & REDIS_MASTER) &&

       c->cmd->flags & REDIS_CMD_WRITE)

    {

       addReply(c, shared.roslaveerr);

       return REDIS_OK;

    }

   

    //对于订阅发布上下文,仅仅只允许执行SUBSCRIBE 和 UNSUBSCRIBE

   /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */

   if (c->flags & REDIS_PUBSUB &&

       c->cmd->proc != pingCommand &&

       c->cmd->proc != subscribeCommand &&

       c->cmd->proc != unsubscribeCommand &&

       c->cmd->proc != psubscribeCommand &&

       c->cmd->proc != punsubscribeCommand) {

       addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUITallowed in this context");

       return REDIS_OK;

    }

 

    //当slave-serve-stale-data未启用并且咱是从服务器和主服务器断开连接,那么只允许执行INFO slaveof

   /* Only allow INFO and SLAVEOF when slave-serve-stale-data is no and

    * we are a slave with a broken link with master. */

   if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED&&

       server.repl_serve_stale_data == 0 &&

       !(c->cmd->flags & REDIS_CMD_STALE))

    {

       flagTransaction(c);

       addReply(c, shared.masterdownerr);

       return REDIS_OK;

}

 

//如果是启动导入数据,如果命令没有REDIS_CMD_LOADING 咱们返回

   /* Loading DB? Return an error if the command has not the

    * REDIS_CMD_LOADING flag. */

   if (server.loading && !(c->cmd->flags &REDIS_CMD_LOADING)) {

       addReply(c, shared.loadingerr);

       return REDIS_OK;

    }

 

    //lua脚本超时,只允许下面执行:authCommand  replconfCommand shutdownCommand

   /* Lua script too slow? Only allow a limited number of commands. */

   if (server.lua_timedout &&

         c->cmd->proc != authCommand &&

         c->cmd->proc != replconfCommand &&

       !(c->cmd->proc == shutdownCommand &&

         c->argc == 2 &&

         tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&

       !(c->cmd->proc == scriptCommand &&

         c->argc == 2 &&

         tolower(((char*)c->argv[1]->ptr)[0]) == 'k'))

    {

       flagTransaction(c);

       addReply(c, shared.slowscripterr);

       return REDIS_OK;

    }

 

        /* Exec the command */

   if (c->flags & REDIS_MULTI &&

       c->cmd->proc != execCommand && c->cmd->proc !=discardCommand &&

       c->cmd->proc != multiCommand && c->cmd->proc !=watchCommand)

{

        //如果是事务命令,除了上面4个,都要入事务队列里面

       queueMultiCommand(c);

       addReply(c,shared.queued);

} else{

 

        //执行常规命令

       call(c,REDIS_CALL_FULL);

       c->woff = server.master_repl_offset;

       if (listLength(server.ready_keys))

           handleClientsBlockedOnLists();

    }

   return REDIS_OK;

}

 

 

//查找命令

struct redisCommand *lookupCommand(sdsname) {

   return dictFetchValue(server.commands, name);

}

//其实就是在字典中根据名字查找

void *dictFetchValue(dict *d, const void*key) {

   dictEntry *he;

   // T = O(1)

   he = dictFind(d,key);

   return he ? dictGetVal(he) : NULL;

}  

 

 

//执行命令

/* Call() is the core of Redis execution ofa command */

void call(redisClient *c, int flags) {

   long long dirty, start, duration;

   int client_old_flags = c->flags;

   

    //在monitor模式下,当不是从aof读取的命令,发送给客户端

   /* Sent the command to clients in MONITOR mode, only if the commands are

    * not generated from reading an AOF. */

   if (listLength(server.monitors) &&

       !server.loading &&

       !(c->cmd->flags & (REDIS_CMD_SKIP_MONITOR|REDIS_CMD_ADMIN)))

   {   

       replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);

   }   

 

   /* Call the command. */

   c->flags &= ~(REDIS_FORCE_AOF|REDIS_FORCE_REPL);

redisOpArrayInit(&server.also_propagate);

//备份旧的计时器值

dirty= server.dirty;

//执行前的时间

start= ustime();

//执行命令

c->cmd->proc(c);

    //执行所耗费的时间

duration= ustime()-start;

//执行后的计时器

   dirty = server.dirty-dirty;

   if (dirty < 0) dirty = 0;

   

//不统计lua的命令

   /* When EVAL is called loading the AOF we don't want commands called

    * from Lua to go into the slowlog or to populate statistics. */

   if (server.loading && c->flags & REDIS_LUA_CLIENT)

       flags &= ~(REDIS_CALL_SLOWLOG | REDIS_CALL_STATS);

 

//如果客户端是lua,根据命令标志和客户端标志打开传播标志

   /* If the caller is Lua, we want to force the EVAL caller to propagate

    * the script if the command flag or client flag are forcing the

    * propagation. */

   if (c->flags & REDIS_LUA_CLIENT && server.lua_caller) {

       if (c->flags & REDIS_FORCE_REPL)

           server.lua_caller->flags |= REDIS_FORCE_REPL;

       if (c->flags & REDIS_FORCE_AOF)

           server.lua_caller->flags |= REDIS_FORCE_AOF;

   }   

                                                            

//需要的时候记录命令日志                                       

   /* Log the command into the Slow log if needed, and populate the

    * per-command statistics that we show in INFO commandstats. */

   if (flags & REDIS_CALL_SLOWLOG && c->cmd->proc !=execCommand) {

       char *latency_event = (c->cmd->flags & REDIS_CMD_FAST) ?

                             "fast-command" : "command";

       latencyAddSampleIfNeeded(latency_event,duration/1000);

       slowlogPushEntryIfNeeded(c->argv,c->argc,duration);

}   

//命令执行时间统计调用次数加1

   if (flags & REDIS_CALL_STATS) {

       c->cmd->microseconds += duration;

       c->cmd->calls++;

    }

 

    //传播和aof处理

   /* Propagate the command into the AOF and replication link */

   if (flags & REDIS_CALL_PROPAGATE) {

       int flags = REDIS_PROPAGATE_NONE;

 

       if (c->flags & REDIS_FORCE_REPL) flags |= REDIS_PROPAGATE_REPL;

       if (c->flags & REDIS_FORCE_AOF) flags |= REDIS_PROPAGATE_AOF;

       if (dirty)

           flags |= (REDIS_PROPAGATE_REPL | REDIS_PROPAGATE_AOF);

       if (flags != REDIS_PROPAGATE_NONE)

           propagate(c->cmd,c->db->id,c->argv,c->argc,flags);

    }

 

   /* Restore the old FORCE_AOF/REPL flags, since call can be executed

    * recursively. */

   c->flags &= ~(REDIS_FORCE_AOF|REDIS_FORCE_REPL);

   c->flags |= client_old_flags &(REDIS_FORCE_AOF|REDIS_FORCE_REPL);

 

   /* Handle the alsoPropagate() API to handle commands that want topropagate

    * multiple separated commands. */

   if (server.also_propagate.numops) {

       int j;

       redisOp *rop;

 

       for (j = 0; j < server.also_propagate.numops; j++) {

           rop = &server.also_propagate.ops[j];

           propagate(rop->cmd, rop->dbid, rop->argv, rop->argc,rop->target);

       }

       redisOpArrayFree(&server.also_propagate);

    }

    server.stat_numcommands++;

}

 

 

你可能感兴趣的:(redis)