/* Objects encoding. Some kind of objects like Strings and Hashes can be * internally represented in multiple ways. The 'encoding' field of the object * is set to one of this fields for this object. */ #define OBJ_ENCODING_RAW 0 /* Raw representation */ #define OBJ_ENCODING_INT 1 /* Encoded as integer */ #define OBJ_ENCODING_HT 2 /* Encoded as hash table */ #define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ #define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */ #define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ #define OBJ_ENCODING_INTSET 6 /* Encoded as intset */ #define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */ #define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */ #define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */ #define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */
/* Encode the length of the previous entry and write it to "p". Return the * number of bytes needed to encode this length if "p" is NULL. */ staticunsignedintzipPrevEncodeLength(unsignedchar *p, unsignedint len) { if (p == NULL) { return (len < ZIP_BIGLEN) ? 1 : sizeof(len)+1; } else { if (len < ZIP_BIGLEN) { p[0] = len; return1; } else { p[0] = ZIP_BIGLEN; memcpy(p+1,&len,sizeof(len)); memrev32ifbe(p+1); return1+sizeof(len); } } }
* |00pppppp| - 1 byte * String value with length less than or equal to 63 bytes (6 bits). * |01pppppp|qqqqqqqq| - 2 bytes * String value with length less than or equal to 16383 bytes (14 bits). * |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes * String value with length greater than or equal to 16384 bytes. * |11000000| - 1 byte * Integer encoded as int16_t(2 bytes). * |11010000| - 1 byte * Integer encoded as int32_t(4 bytes). * |11100000| - 1 byte * Integer encoded as int64_t(8 bytes). * |11110000| - 1 byte * Integer encoded as 24 bit signed(3 bytes). * |11111110| - 1 byte * Integer encoded as 8 bit signed(1 byte). * |1111xxxx| - (with xxxx between 0000 and 1101) immediate 4 bit integer. * Unsigned integer from 0 to 12. The encoded value is actually from * 1 to 13 because 0000 and 1111 can not be used, so 1 should be * subtracted from the encoded 4 bit value to obtain the right value. * |11111111| - End of ziplist.
/* Extract the encoding from the byte pointed by 'ptr' and set it into * 'encoding'. */ #define ZIP_ENTRY_ENCODING(ptr, encoding) do { \ (encoding) = (ptr[0]); \ if ((encoding) < ZIP_STR_MASK) (encoding) &= ZIP_STR_MASK; \ } while(0)
/* Decode the length encoded in 'ptr'. The 'encoding' variable will hold the * entries encoding, the 'lensize' variable will hold the number of bytes * required to encode the entries length, and the 'len' variable will hold the * entries length. */ #define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do { \ ZIP_ENTRY_ENCODING((ptr), (encoding)); \ if ((encoding) < ZIP_STR_MASK) { \ if ((encoding) == ZIP_STR_06B) { \ (lensize) = 1; \ (len) = (ptr)[0] & 0x3f; \ } elseif ((encoding) == ZIP_STR_14B) { \ (lensize) = 2; \ (len) = (((ptr)[0] & 0x3f) << 8) | (ptr)[1]; \ } elseif (encoding == ZIP_STR_32B) { \ (lensize) = 5; \ (len) = ((ptr)[1] << 24) | \ ((ptr)[2] << 16) | \ ((ptr)[3] << 8) | \ ((ptr)[4]); \ } else { \ assert(NULL); \ } \ } else { \ (lensize) = 1; \ (len) = zipIntSize(encoding); \ } \ } while(0);
/* Encode the length 'rawlen' writing it in 'p'. If p is NULL it just returns * the amount of bytes required to encode such a length. */ staticunsignedintzipEncodeLength(unsignedchar *p, unsignedchar encoding, unsignedint rawlen) { unsignedchar len = 1, buf[5];
if (ZIP_IS_STR(encoding)) { /* Although encoding is given it may not be set for strings, * so we determine it here using the raw length. */ if (rawlen <= 0x3f) { if (!p) return len; buf[0] = ZIP_STR_06B | rawlen; } elseif (rawlen <= 0x3fff) { len += 1; if (!p) return len; buf[0] = ZIP_STR_14B | ((rawlen >> 8) & 0x3f); buf[1] = rawlen & 0xff; } else { len += 4; if (!p) return len; buf[0] = ZIP_STR_32B; buf[1] = (rawlen >> 24) & 0xff; buf[2] = (rawlen >> 16) & 0xff; buf[3] = (rawlen >> 8) & 0xff; buf[4] = rawlen & 0xff; } } else { /* Implies integer encoding, so length is always 1. */ if (!p) return len; buf[0] = encoding; }
/* Store this length at p */ memcpy(p,buf,len); return len; }
可以看到,对于integer编码,长度恒为1,否则读取实际的string的长度值。
而实际上,encoding又是保存在len字段的第一个字节,判断是否是字符串的方法如下:
1 2 3
#define ZIP_STR_MASK 0xc0 /* Macro to determine type */ #define ZIP_IS_STR(enc) (((enc) & ZIP_STR_MASK) < ZIP_STR_MASK)
/* Check if string pointed to by 'entry' can be encoded as an integer. * Stores the integer value in 'v' and its encoding in 'encoding'. */ staticintzipTryEncoding(unsignedchar *entry, unsignedint entrylen, longlong *v, unsignedchar *encoding) { longlong value;
if (entrylen >= 32 || entrylen == 0) return0; if (string2ll((char*)entry,entrylen,&value)) { /* Great, the string can be encoded. Check what's the smallest * of our encoding types that can hold this value. */ if (value >= 0 && value <= 12) { *encoding = ZIP_INT_IMM_MIN+value; } elseif (value >= INT8_MIN && value <= INT8_MAX) { *encoding = ZIP_INT_8B; } elseif (value >= INT16_MIN && value <= INT16_MAX) { *encoding = ZIP_INT_16B; } elseif (value >= INT24_MIN && value <= INT24_MAX) { *encoding = ZIP_INT_24B; } elseif (value >= INT32_MIN && value <= INT32_MAX) { *encoding = ZIP_INT_32B; } else { *encoding = ZIP_INT_64B; } *v = value; return1; } return0; }
/* Convert a string into a long long. Returns 1 if the string could be parsed * into a (non-overflowing) long long, 0 otherwise. The value will be set to * the parsed value when appropriate. */ intstring2ll(constchar *s, size_t slen, longlong *value);
/* When an entry is inserted, we need to set the prevlen field of the next * entry to equal the length of the inserted entry. It can occur that this * length cannot be encoded in 1 byte and the next entry needs to be grow * a bit larger to hold the 5-byte encoded prevlen. This can be done for free, * because this only happens when an entry is already being inserted (which * causes a realloc and memmove). However, encoding the prevlen may require * that this entry is grown as well. This effect may cascade throughout * the ziplist when there are consecutive entries with a size close to * ZIP_BIGLEN, so we need to check that the prevlen can be encoded in every * consecutive entry. * * Note that this effect can also happen in reverse, where the bytes required * to encode the prevlen field can shrink. This effect is deliberately ignored, * because it can cause a "flapping" effect where a chain prevlen fields is * first grown and then shrunk again after consecutive inserts. Rather, the * field is allowed to stay larger than necessary, because a large prevlen * field implies the ziplist is holding large entries anyway. * * The pointer "p" points to the first entry that does NOT need to be * updated, i.e. consecutive fields MAY need an update. */ staticunsignedchar *__ziplistCascadeUpdate(unsignedchar *zl, unsignedchar *p);
/* Node, quicklist, and Iterator are the only data structures used currently. */
/* quicklistNode is a 32 byte struct describing a ziplist for a quicklist. * We use bit fields keep the quicklistNode at 32 bytes. * count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k). * encoding: 2 bits, RAW=1, LZF=2. * container: 2 bits, NONE=1, ZIPLIST=2. * recompress: 1 bit, bool, true if node is temporarry decompressed for usage. * attempted_compress: 1 bit, boolean, used for verifying during testing. * extra: 10 bits, free for future use; pads out the remainder of 32 bits */ typedefstructquicklistNode { structquicklistNode *prev; structquicklistNode *next; unsignedchar *zl; unsignedint sz; /* ziplist size in bytes */ unsignedint count : 16; /* count of items in ziplist */ unsignedint encoding : 2; /* RAW==1 or LZF==2 */ unsignedint container : 2; /* NONE==1 or ZIPLIST==2 */ unsignedint recompress : 1; /* was this node previous compressed? */ unsignedint attempted_compress : 1; /* node can't compress; too small */ unsignedint extra : 10; /* more bits to steal for future usage */ } quicklistNode;
/* quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'. * 'sz' is byte length of 'compressed' field. * 'compressed' is LZF data with total (compressed) length 'sz' * NOTE: uncompressed length is stored in quicklistNode->sz. * When quicklistNode->zl is compressed, node->zl points to a quicklistLZF */ typedefstructquicklistLZF { unsignedint sz; /* LZF size in bytes*/ char compressed[]; } quicklistLZF;
/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist. * 'count' is the number of total entries. * 'len' is the number of quicklist nodes. * 'compress' is: -1 if compression disabled, otherwise it's the number * of quicklistNodes to leave uncompressed at ends of quicklist. * 'fill' is the user-requested (or default) fill factor. */ typedefstructquicklist { quicklistNode *head; quicklistNode *tail; unsignedlong count; /* total count of all entries in all ziplists */ unsignedlong len; /* number of quicklistNodes */ int fill : 16; /* fill factor for individual nodes */ unsignedint compress : 16; /* depth of end nodes not to compress;0=off */ } quicklist;
/* Maximum size in bytes of any multi-element ziplist. * Larger values will live in their own isolated ziplists. */ #define SIZE_SAFETY_LIMIT 8192 #define sizeMeetsSafetyLimit(sz) ((sz) <= SIZE_SAFETY_LIMIT)
REDIS_STATIC int _quicklistNodeAllowInsert(const quicklistNode *node, constint fill, constsize_t sz) { if (unlikely(!node)) return0;
int ziplist_overhead; /* size of previous offset */ if (sz < 254) ziplist_overhead = 1; else ziplist_overhead = 5;
/* Minimum ziplist size in bytes for attempting compression. */ #define MIN_COMPRESS_BYTES 48
/* Compress the ziplist in 'node' and update encoding details. * Returns 1 if ziplist compressed successfully. * Returns 0 if compression failed or if ziplist too small to compress. */ REDIS_STATIC int __quicklistCompressNode(quicklistNode *node) { #ifdef REDIS_TEST node->attempted_compress = 1; #endif
/* Don't bother compressing small values */ if (node->sz < MIN_COMPRESS_BYTES) return0;
/* Uncompress the ziplist in 'node' and update encoding details. * Returns 1 on successful decode, 0 on failure to decode. */ REDIS_STATIC int __quicklistDecompressNode(quicklistNode *node) { #ifdef REDIS_TEST node->attempted_compress = 0; #endif
void *decompressed = zmalloc(node->sz); quicklistLZF *lzf = (quicklistLZF *)node->zl; if (lzf_decompress(lzf->compressed, lzf->sz, decompressed, node->sz) == 0) { /* Someone requested decompress, but we can't decompress. Not good. */ zfree(decompressed); return0; } zfree(lzf); node->zl = decompressed; node->encoding = QUICKLIST_NODE_ENCODING_RAW; return1; }
$ redis-benchmark -n 1000000 lpush mylist "mylist str len: 463. Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, while in traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial" ====== lpush mylist str len: 463. Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, whilein traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial ====== 1000000 requests completed in 14.27 seconds 50 parallel clients 3 bytes payload keep alive: 1
$ redis-benchmark -n 1000000 lpush mylist "mylist str len: 463. Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, while in traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial" ====== lpush mylist str len: 463. Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, whilein traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial ====== 1000000 requests completed in 13.99 seconds 50 parallel clients 3 bytes payload keep alive: 1
# Similarly to hashes, small lists are also encoded in a special way in order # to save a lot of space. The special representation is only used when # you are under the following limits: list-max-ziplist-entries 512 list-max-ziplist-value 64
if (enc == REDIS_ENCODING_LINKEDLIST) { list *l = listCreate(); listSetFreeMethod(l,decrRefCountVoid);
/* listTypeGet returns a robj with incremented refcount */ li = listTypeInitIterator(subject,0,REDIS_TAIL); while (listTypeNext(li,&entry)) listAddNodeTail(l,listTypeGet(&entry)); listTypeReleaseIterator(li);
$ redis-benchmark -n 1000000 lpush mylist "mylist str len: 463. Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, while in traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial" ====== lpush mylist mylist str len: 463. Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, whilein traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial ====== 1000000 requests completed in 14.24 seconds 50 parallel clients 3 bytes payload keep alive: 1
$ redis-benchmark -n 100 lpush mylist "mylist str len: 463. Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, while in traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial"
====== lpush mylist mylist str len: 463. Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, whilein traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial ====== 100 requests completed in 0.00 seconds 50 parallel clients 3 bytes payload keep alive: 1
20.00% <= 1 milliseconds 100.00% <= 1 milliseconds 25000.00 requests per second
$ redis-benchmark -n 100000 lpush mylist "mylist str len: 463. Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, while in traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial"
====== lpush mylist mylist str len: 463. Redis is not a plain key-value store, it is actually a data structures server, supporting different kinds of values. What this means is that, whilein traditional key-value stores you associated string keys to string values, in Redis the value is not limited to a simple string, but can also hold more complex data structures. The following is the list of all the data structures supported by Redis, which will be covered separately in this tutorial ====== 100000 requests completed in 361.97 seconds 50 parallel clients 3 bytes payload keep alive: 1