zoukankan      html  css  js  c++  java
  • 【redis源码】(三)Zipmap

    更科学的分析见这篇文章,小弟看了之后才明白一点点的

    http://www.open-open.com/bbs/view/1321697543077

    下面几句是我自己对zipmap的理解

    1. zipmap是一个简单的使用字符串实现的hash表,按照作者的本意,只会包含254以内的的key-value对

    2. 整个redis来看,redis是一个巨大地hash表【key-value对】,只不过这个大hash表的value不只支持字符串,而是redisObject,redisObject中的void * ptr指针可以指向任何数据结构~~~【强悍的扩展性,咱可以自己添加】

    3. dict乃是redis实现hash表的数据结构,这是个复杂的东西【如果entry量很小,用起来效率很低】,作者为了节省内存,当redisobject为一个hashmap并且entry数量很小【<254】,value会使用轻量级的zipmap作为map的承载体. zip本身的操作复杂度是O(n)【n为key-value对的数量】

    哦了~ 贴代码,只是加了一些本人的注释,没啥特别的,供自己做个记录用~

    Ziphash.h

     1 #ifndef _ZIMMAP_H
     2 #define _ZIPMAP_H
     3 
     4 unsigned char *zipmapNew(void);
     5 unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update);
     6 unsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted);
     7 unsigned char *zipmapRewind(unsigned char *zm);
     8 unsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen);
     9 int zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen);
    10 int zipmapExists(unsigned char *zm, unsigned char *key, unsigned int klen);
    11 unsigned int zipmapLen(unsigned char *zm);
    12 void zipmapRepr(unsigned char *p);
    13 
    14 #endif

    Ziphash.c

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <assert.h>
      4 #include "zmalloc.h"
      5 
      6 //如果len字段为254,则后4个字节表示真正长度
      7 #define ZIPMAP_BIGLEN 254
      8 //zipmap串最后一个byte的值
      9 #define ZIPMAP_END 255
     10 
     11 /* The following defines the max value for the <free> field described in the
     12  * comments above, that is, the max number of trailing bytes in a value. */
     13 //如果free字段大于此值,则进行压缩操作,节省空间
     14 #define ZIPMAP_VALUE_MAX_FREE 4
     15 
     16 /* The following macro returns the number of bytes needed to encode the length
     17  * for the integer value _l, that is, 1 byte for lengths < ZIPMAP_BIGLEN and
     18  * 5 bytes for all the other lengths. */
     19 //这个宏用来计算表达len的真实长度,如果小于254,则len占用1个字节,否则占用4+1个字节
     20 #define ZIPMAP_LEN_BYTES(_l) (((_l) < ZIPMAP_BIGLEN) ? 1 : sizeof(unsigned int)+1)
     21 
     22 //创建一个新的zipmap,这个空zipmap占用2个byte的内存空间
     23 /* Create a new empty zipmap. */
     24 unsigned char *zipmapNew(void) {
     25     unsigned char *zm = zmalloc(2);
     26 
     27     zm[0] = 0; /* Length */
     28     zm[1] = ZIPMAP_END;
     29     return zm;
     30 }
     31 
     32 
     33 //如果长度p所在的unsigned char的小于254,即使后边跟随的string的长度,如果大于等于254,即后边的4个bytes为真实长度
     34 /* Decode the encoded length pointed by 'p' */
     35 static unsigned int zipmapDecodeLength(unsigned char *p) {
     36     unsigned int len = *p;
     37 
     38     if (len < ZIPMAP_BIGLEN) return len;
     39     memcpy(&len,p+1,sizeof(unsigned int));
     40     return len;
     41 }
     42 
     43 
     44 //输入value或者key的长度len,将zipmap自有的长度表示方法的字符串放到p开头的字符串中,如果p为NULL直接返回占用的byte字节数
     45 /* Encode the length 'l' writing it in 'p'. If p is NULL it just returns
     46  * the amount of bytes required to encode such a length. */
     47 static unsigned int zipmapEncodeLength(unsigned char *p, unsigned int len) {
     48     if (p == NULL) {
     49         return ZIPMAP_LEN_BYTES(len);
     50     } else {
     51         if (len < ZIPMAP_BIGLEN) {
     52             p[0] = len;
     53             return 1;
     54         } else {
     55             p[0] = ZIPMAP_BIGLEN;
     56             memcpy(p+1,&len,sizeof(len));
     57             return 1+sizeof(len);
     58         }
     59     }
     60 }
     61 
     62 //在zm中寻找长度为klen的key,如果找到,返回其所为key的<len>字段的指针,如果没找到返回NULL.
     63 //如果totlen字段不为NULL,则使其为整个zm所占的bytes数
     64 /* Search for a matching key, returning a pointer to the entry inside the
     65  * zipmap. Returns NULL if the key is not found.
     66  *
     67  * If NULL is returned, and totlen is not NULL, it is set to the entire
     68  * size of the zimap, so that the calling function will be able to
     69  * reallocate the original zipmap to make room for more entries. */
     70 static unsigned char *zipmapLookupRaw(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned int *totlen) {
     71     unsigned char *p = zm+1, *k = NULL;
     72     unsigned int l,llen;
     73 
     74     while(*p != ZIPMAP_END) { //如果未到结尾
     75         unsigned char free;
     76 
     77         /* Match or skip the key */
     78         l = zipmapDecodeLength(p); //得到value或者key长度大小
     79         llen = zipmapEncodeLength(NULL,l); // 得到len本身占的byte数
     80         //如果长度和cmp结果为0(相同)
     81         if (k == NULL && l == klen && !memcmp(p+llen,key,l)) {
     82             /* Only return when the user doesn't care
     83              * for the total length of the zipmap. */
     84             if (totlen != NULL) {
     85                 k = p;
     86             } else { //如果totlen为NULL直接返回指针p
     87                 return p;
     88             }
     89         }
     90         p += llen+l;
     91         /* Skip the value as well */
     92         l = zipmapDecodeLength(p);
     93         p += zipmapEncodeLength(NULL,l);
     94         free = p[0];
     95         p += l+1+free; /* +1 to skip the free byte */
     96     }
     97     if (totlen != NULL) *totlen = (unsigned int)(p-zm)+1;
     98     return k;
     99 }
    100 
    101 
    102 //得到一个只保存一个keyvalue对的zipmap所需要的bytes数
    103 static unsigned long zipmapRequiredLength(unsigned int klen, unsigned int vlen) {
    104     unsigned int l;
    105 
    106     l = klen+vlen+3; //key_len+value_len+value_free
    107     if (klen >= ZIPMAP_BIGLEN) l += 4; 
    108     if (vlen >= ZIPMAP_BIGLEN) l += 4;
    109     return l;
    110 }
    111 
    112 //得到一个key所需要的内存大小
    113 /* Return the total amount used by a key (encoded length + payload) */
    114 static unsigned int zipmapRawKeyLength(unsigned char *p) {
    115     unsigned int l = zipmapDecodeLength(p);
    116     return zipmapEncodeLength(NULL,l) + l;
    117 }
    118 
    119 //p为value的首地址,计算value段占用的内存bytes数
    120 /* Return the total amount used by a value
    121  * (encoded length + single byte free count + payload) */
    122 static unsigned int zipmapRawValueLength(unsigned char *p) {
    123     unsigned int l = zipmapDecodeLength(p);
    124     unsigned int used;
    125     
    126     used = zipmapEncodeLength(NULL,l);
    127     used += p[used] + 1 + l; //p[used] free的字节数+ free本身一个字节+len长度(1 or 5)
    128     return used;
    129 }
    130 
    131 //返回整个p指向的key-value对所占用的内存空间大小
    132 /* If 'p' points to a key, this function returns the total amount of
    133  * bytes used to store this entry (entry = key + associated value + trailing
    134  * free space if any). */
    135 static unsigned int zipmapRawEntryLength(unsigned char *p) {
    136     unsigned int l = zipmapRawKeyLength(p);
    137     return l + zipmapRawValueLength(p+l);
    138 }
    139 
    140 
    141 //realloc 内存for zm
    142 static inline unsigned char *zipmapResize(unsigned char *zm, unsigned int len) {
    143     zm = zrealloc(zm, len);
    144     zm[len-1] = ZIPMAP_END;
    145     return zm;
    146 }
    147 
    148 //插入一个key-value对,如果key不存在,则直接resize zm,加到最后边的位置
    149 //如果key存在,并且空间够用,则直接在原位置对value进行替换,更新free,如果free小于ZIPMAP_VALUE_MAX_FREE,则保留free,如果大于等于ZIPMAP_VALUE_MAX_FREE,则需要尽心memmove
    150 /* Set key to value, creating the key if it does not already exist.
    151  * If 'update' is not NULL, *update is set to 1 if the key was
    152  * already preset, otherwise to 0. */
    153 unsigned char *zipmapSet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char *val, unsigned int vlen, int *update) {
    154     unsigned int zmlen, offset;
    155     unsigned int freelen, reqlen = zipmapRequiredLength(klen,vlen);
    156     unsigned int empty, vempty;
    157     unsigned char *p;
    158    
    159     freelen = reqlen;
    160     if (update) *update = 0;
    161     p = zipmapLookupRaw(zm,key,klen,&zmlen);
    162     if (p == NULL) {
    163         /* Key not found: enlarge */
    164         zm = zipmapResize(zm, zmlen+reqlen);
    165         p = zm+zmlen-1;
    166         zmlen = zmlen+reqlen;
    167 
    168         /* Increase zipmap length (this is an insert) */
    169         if (zm[0] < ZIPMAP_BIGLEN) zm[0]++;
    170     } else {
    171         /* Key found. Is there enough space for the new value? */
    172         /* Compute the total length: */
    173         if (update) *update = 1;
    174         freelen = zipmapRawEntryLength(p);
    175         if (freelen < reqlen) {
    176             /* Store the offset of this key within the current zipmap, so
    177              * it can be resized. Then, move the tail backwards so this
    178              * pair fits at the current position. */
    179             offset = p-zm;
    180             zm = zipmapResize(zm, zmlen-freelen+reqlen);
    181             p = zm+offset;
    182 
    183             /* The +1 in the number of bytes to be moved is caused by the
    184              * end-of-zipmap byte. Note: the *original* zmlen is used. */
    185             memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));
    186             zmlen = zmlen-freelen+reqlen;
    187             freelen = reqlen;
    188         }
    189     }
    190 
    191     /* We now have a suitable block where the key/value entry can
    192      * be written. If there is too much free space, move the tail
    193      * of the zipmap a few bytes to the front and shrink the zipmap,
    194      * as we want zipmaps to be very space efficient. */
    195     empty = freelen-reqlen;
    196     if (empty >= ZIPMAP_VALUE_MAX_FREE) {
    197         /* First, move the tail <empty> bytes to the front, then resize
    198          * the zipmap to be <empty> bytes smaller. */
    199         offset = p-zm;
    200         memmove(p+reqlen, p+freelen, zmlen-(offset+freelen+1));
    201         zmlen -= empty;
    202         zm = zipmapResize(zm, zmlen);
    203         p = zm+offset;
    204         vempty = 0;
    205     } else {
    206         vempty = empty;
    207     }
    208 
    209     /* Just write the key + value and we are done. */
    210     /* Key: */
    211     p += zipmapEncodeLength(p,klen);
    212     memcpy(p,key,klen);
    213     p += klen;
    214     /* Value: */
    215     p += zipmapEncodeLength(p,vlen);
    216     *p++ = vempty;
    217     memcpy(p,val,vlen);
    218     return zm;
    219 }
    220 
    221 
    222 //删除指定key,并用deleted来indicated执行结果
    223 /* Remove the specified key. If 'deleted' is not NULL the pointed integer is
    224  * set to 0 if the key was not found, to 1 if it was found and deleted. */
    225 unsigned char *zipmapDel(unsigned char *zm, unsigned char *key, unsigned int klen, int *deleted) {
    226     unsigned int zmlen, freelen;
    227     unsigned char *p = zipmapLookupRaw(zm,key,klen,&zmlen);
    228     if (p) {
    229         freelen = zipmapRawEntryLength(p);
    230         memmove(p, p+freelen, zmlen-((p-zm)+freelen+1));
    231         zm = zipmapResize(zm, zmlen-freelen);
    232 
    233         /* Decrease zipmap length */
    234         if (zm[0] < ZIPMAP_BIGLEN) zm[0]--;
    235 
    236         if (deleted) *deleted = 1;
    237     } else {
    238         if (deleted) *deleted = 0;
    239     }
    240     return zm;
    241 }
    242 
    243 /* Call it before to iterate trought elements via zipmapNext() */
    244 unsigned char *zipmapRewind(unsigned char *zm) {
    245     return zm+1;
    246 }
    247 
    248 /* This function is used to iterate through all the zipmap elements.
    249  * In the first call the first argument is the pointer to the zipmap + 1.
    250  * In the next calls what zipmapNext returns is used as first argument.
    251  * Example:
    252  *
    253  * unsigned char *i = zipmapRewind(my_zipmap);
    254  * while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) {
    255  *     printf("%d bytes key at $p\n", klen, key);
    256  *     printf("%d bytes value at $p\n", vlen, value);
    257  * }
    258  */
    259 unsigned char *zipmapNext(unsigned char *zm, unsigned char **key, unsigned int *klen, unsigned char **value, unsigned int *vlen) {
    260     if (zm[0] == ZIPMAP_END) return NULL;
    261     if (key) {
    262         *key = zm;
    263         *klen = zipmapDecodeLength(zm);
    264         *key += ZIPMAP_LEN_BYTES(*klen);
    265     }
    266     zm += zipmapRawKeyLength(zm);
    267     if (value) {
    268         *value = zm+1;
    269         *vlen = zipmapDecodeLength(zm);
    270         *value += ZIPMAP_LEN_BYTES(*vlen);
    271     }
    272     zm += zipmapRawValueLength(zm);
    273     return zm;
    274 }
    275 
    276 //取得一个key对应的value,如果找到key,返回1,否则返回0
    277 /* Search a key and retrieve the pointer and len of the associated value.
    278  * If the key is found the function returns 1, otherwise 0. */
    279 int zipmapGet(unsigned char *zm, unsigned char *key, unsigned int klen, unsigned char **value, unsigned int *vlen) {
    280     unsigned char *p;
    281 
    282     if ((p = zipmapLookupRaw(zm,key,klen,NULL)) == NULL) return 0;
    283     p += zipmapRawKeyLength(p);
    284     *vlen = zipmapDecodeLength(p);
    285     *value = p + ZIPMAP_LEN_BYTES(*vlen) + 1;
    286     return 1;
    287 }
    288 
    289 //判断一个key是否存在于zm中
    290 /* Return 1 if the key exists, otherwise 0 is returned. */
    291 int zipmapExists(unsigned char *zm, unsigned char *key, unsigned int klen) {
    292     return zipmapLookupRaw(zm,key,klen,NULL) != NULL;
    293 }
    294 
    295 /* Return the number of entries inside a zipmap */
    296 unsigned int zipmapLen(unsigned char *zm) {
    297     unsigned int len = 0;
    298     if (zm[0] < ZIPMAP_BIGLEN) {
    299         len = zm[0];
    300     } else {
    301         unsigned char *p = zipmapRewind(zm);
    302         while((p = zipmapNext(p,NULL,NULL,NULL,NULL)) != NULL) len++;
    303 
    304         /* Re-store length if small enough */
    305         if (len < ZIPMAP_BIGLEN) zm[0] = len;
    306     }
    307     return len;
    308 }
    309 
    310 //打印p开头的kye-value对
    311 void zipmapRepr(unsigned char *p) {
    312     unsigned int l;
    313 
    314     printf("{status %u}",*p++);
    315     while(1) {
    316         if (p[0] == ZIPMAP_END) {
    317             printf("{end}");
    318             break;
    319         } else {
    320             unsigned char e;
    321 
    322             l = zipmapDecodeLength(p);
    323             printf("{key %u}",l);
    324             p += zipmapEncodeLength(NULL,l);
    325             if (l != 0 && fwrite(p,l,1,stdout) == 0) perror("fwrite");
    326             p += l;
    327 
    328             l = zipmapDecodeLength(p);
    329             printf("{value %u}",l);
    330             p += zipmapEncodeLength(NULL,l);
    331             e = *p++;
    332             if (l != 0 && fwrite(p,l,1,stdout) == 0) perror("fwrite");
    333             p += l+e;
    334             if (e) {
    335                 printf("[");
    336                 while(e--) printf(".");
    337                 printf("]");
    338             }
    339         }
    340     }
    341     printf("\n");
    342 }
    343 
    344 #ifdef ZIPMAP_TEST_MAIN
    345 int main(void) {
    346     unsigned char *zm;
    347 
    348     zm = zipmapNew();
    349 
    350     zm = zipmapSet(zm,(unsigned char*) "name",4, (unsigned char*) "foo",3,NULL);
    351     zm = zipmapSet(zm,(unsigned char*) "surname",7, (unsigned char*) "foo",3,NULL);
    352     zm = zipmapSet(zm,(unsigned char*) "age",3, (unsigned char*) "foo",3,NULL);
    353     zipmapRepr(zm);
    354 
    355     zm = zipmapSet(zm,(unsigned char*) "hello",5, (unsigned char*) "world!",6,NULL);
    356     zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "bar",3,NULL);
    357     zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "!",1,NULL);
    358     zipmapRepr(zm);
    359     zm = zipmapSet(zm,(unsigned char*) "foo",3, (unsigned char*) "12345",5,NULL);
    360     zipmapRepr(zm);
    361     zm = zipmapSet(zm,(unsigned char*) "new",3, (unsigned char*) "xx",2,NULL);
    362     zm = zipmapSet(zm,(unsigned char*) "noval",5, (unsigned char*) "",0,NULL);
    363     zipmapRepr(zm);
    364     zm = zipmapDel(zm,(unsigned char*) "new",3,NULL);
    365     zipmapRepr(zm);
    366 
    367     printf("\nLook up large key:\n");
    368     {
    369         unsigned char buf[512];
    370         unsigned char *value;
    371         unsigned int vlen, i;
    372         for (i = 0; i < 512; i++) buf[i] = 'a';
    373 
    374         zm = zipmapSet(zm,buf,512,(unsigned char*) "long",4,NULL);
    375         if (zipmapGet(zm,buf,512,&value,&vlen)) {
    376             printf("  <long key> is associated to the %d bytes value: %.*s\n",
    377                 vlen, vlen, value);
    378         }
    379     }
    380 
    381     printf("\nPerform a direct lookup:\n");
    382     {
    383         unsigned char *value;
    384         unsigned int vlen;
    385 
    386         if (zipmapGet(zm,(unsigned char*) "foo",3,&value,&vlen)) {
    387             printf("  foo is associated to the %d bytes value: %.*s\n",
    388                 vlen, vlen, value);
    389         }
    390     }
    391     printf("\nIterate trought elements:\n");
    392     {
    393         unsigned char *i = zipmapRewind(zm);
    394         unsigned char *key, *value;
    395         unsigned int klen, vlen;
    396 
    397         while((i = zipmapNext(i,&key,&klen,&value,&vlen)) != NULL) {
    398             printf("  %d:%.*s => %d:%.*s\n", klen, klen, key, vlen, vlen, value);
    399         }
    400     }
    401     return 0;
    402 }
    403 #endif

    下一篇将介绍ziplist~~

    喜欢一起简单,实用的东西,拒绝复杂花哨,我不是GEEK.
  • 相关阅读:
    nmon监控及分析(转)
    Python资源大全
    pyqt4使用简易笔记
    windows下 使用pyinstaller 打包os.Popen()问题
    用pyautogui操作windows
    jmeter 报错:java.net.BindException: Address already in use: connect
    jmeter 报错Non HTTP response code: org.apache.http.conn.ConnectTimeoutException
    vue父子组件通信
    centos6.7安装mysql-5.7
    linux下 多python环境 修改默认python2为python3
  • 原文地址:https://www.cnblogs.com/igloo1986/p/2658730.html
Copyright © 2011-2022 走看看