redis代码结构之三类型库-list

来源:互联网 发布:淘宝工具软件 编辑:程序博客网 时间:2024/06/05 05:47

redis代码结构之三类型库-list 

1. 类型库概述

下面介绍redis核心的内容以及所支持的数据类型及操作。首先看一下相应的数据结构:

[cpp] view plaincopy
  1. typedef char *sds; //该文件返回的基本上都是sds,即char *,也是实际存储内容的地址。  
  2. struct sdshdr {  
  3.     int len; //内容拥有的空间,不包括该结构的本身的大小,也不包括buf最后的结束符’\0’  
  4.     int free; //剩下的可用内存空间,buf中可能有一些没有使用,这些的长度就是free的值  
  5.     char buf[]; //实际的存储内容=strlen(buf)+free=len  
  6. };  

该类型是redis使用最多的类型,它类似c++的string类型。相应的函数包括sds sdsnewlen(const void *init, size_t initlen) ,它首先申请sh = zmalloc(sizeof(struct sdshdr)+initlen+1);内存(注:sizeof(struct sdshdr)=8),initlen就是sdshdr->len,而init则会被memcpy到sdshdr->buf里,并且此时sdshdr->free为0。最后返回(char*)sh->buf,即实际内容的地址。其它的就类似于string的方法。 

[cpp] view plaincopy
  1. typedef struct redisObject {  
  2.     unsigned type:4;  
  3.     unsigned storage:2;     /* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */  
  4.     unsigned encoding:4;  
  5.     unsigned lru:22;        /* lru time (relative to server.lruclock) */  
  6.     int refcount;  
  7.     void *ptr;  
  8. } robj;  

该结构定义了redis的操作对象的抽象数据类型。
Type:定义该数据的类型,当前的redis支持5种数据类型:REDIS_STRING、REDIS_LIST、REDIS_SET、REDIS_ZSET、REDIS_HASH。
Storage:定义数据当前的位置内存还是硬盘的swap。
Encoding:定义数据类型使用的编码方式或者叫内存的存储方式,当前redis支持8种encoding。各种类型及所支持的编码见下表:

类型

REDIS_STRING(t_string.c)

REDIS_LIST(t_list.c,ziplist.c)

REDIS_SET(t_set.c)

REDIS_ZSET(t_zset.c)

REDIS_HASH(t_hash.c)

命令

set,mset

lpush,rpush,lpop,rpop

sadd

zadd

Hset,hmset

编码

REDIS_ENCODING_RAW, REDIS_ENCODING_INT

REDIS_ENCODING_ZIPLIST, REDIS_ENCODING_LINKEDLIST

REDIS_ENCODING_HT, REDIS_ENCODING_INTSET

REDIS_ENCODING_ZIPLIST, REDIS_ENCODING_SKIPLIST

REDIS_ENCODING_HT, REDIS_ENCODING_ZIPMAP

数据的类型是由用户的命令决定的,另外所有的key都使用REDIS_STRING类型(在dbAdd的时候再提取出实际的内容sds copy = sdsdup(key->ptr);,这个就是真正保存到dict里的key值),并且它的encoding只为REDIS_ENCODING_RAW;而编码则是由server根据配置及当前的系统情况进行选择的(这个条件我们在后面的每种类型中分别介绍)。
Lru:
Refcount:该对象的引用个数
Ptr:指向实际存储的内容
Server在接收到client请求的时候总是先把命令的所有成员转换成type=REDIS_STRING,encoding = REDIS_ENCODING_RAW为的robj类型保存到client->argv[]里(0是命令,1是key,2,3..是value【2可能是expire值】,这个过程在processInlineBuffer或者processMultibulkBuffer函数里完成)。同时在调用每个命令相应的处理函数的时候,大多数命令都会对value先进行一个tryObjectEncoding,即判断该value是否可表示为long类型string2l(s,sdslen(s),&value),如果可以的话就使用shared.integers[value],或者encoding = REDIS_ENCODING_INT,代码如下:

[cpp] view plaincopy
  1. if (!string2l(s,sdslen(s),&value)) return o;  
  2. …  
  3. if (server.maxmemory == 0 && value >= 0 && value < REDIS_SHARED_INTEGERS &&  
  4.    pthread_equal(pthread_self(),server.mainthread)) {  
  5.     decrRefCount(o);  
  6.       incrRefCount(shared.integers[value]);  
  7.       return shared.integers[value];  
  8. else {  
  9.      o->encoding = REDIS_ENCODING_INT;  
  10.      sdsfree(o->ptr);  
  11.      o->ptr = (void*) value;  
  12.      return o;  
  13. }  
  14. 下面我们分别介绍五种数据类型的相应结构及操作。(每种类型的操作指令及含义可参考<a href="http://www.w3ccollege.org/redis/redis-command-manual.html">http://www.w3ccollege.org/redis/redis-command-manual.html</a>)  
  15. 2. REDIS_STRING(t_string.c)该类型的命令包括:set,setnx,setex,mset,incr,append等等。这里我们只介绍set命令,它相应的命令回调函数为  
  16. <pre class="cpp" name="code">setCommand(redisClient *c) {  
  17.     c->argv[2] = tryObjectEncoding(c->argv[2]);  
  18.     setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);  
  19. }</pre>该类型的大多数命令都是通过调用setGenericCommand函数为内部接口:<br>  
  20. <pre class="cpp" name="code">void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) {  
  21.     long seconds = 0; /* initialized to avoid an harmness warning */  
  22.   
  23.     if (expire) {//设置过期时间的操作  
  24.         if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK)  
  25.             return;  
  26.         if (seconds <= 0) {  
  27.             addReplyError(c,"invalid expire time in SETEX");  
  28.             return;  
  29.         }  
  30.     }  
  31.     //如果该key存在,并且命令要求在key不存在时才set,此时则返回  
  32.     if (lookupKeyWrite(c->db,key) != NULL && nx) {  
  33.         addReply(c,shared.czero);  
  34.         return;  
  35.     }  
  36.     setKey(c->db,key,val); //向字典中追加key,val  
  37.     server.dirty++;//设置dirty持久化时使用  
  38.     if (expire) setExpire(c->db,key,time(NULL)+seconds);//向expire dict中添加该事件  
  39.     addReply(c, nx ? shared.cone : shared.ok);  
  40. }</pre>这就是set操作的过程,很简单。其内部的setKey就是调用db.c,dict.c的函数。  
  41. <pre></pre>  
  42. <pre></pre>  
0 0
原创粉丝点击