o->type = type; o
->encoding = OBJ_ENCODING_RAW; o
->ptr = ptr; o
->refcount = 1;
/* Set the LRU to the current lruclock (minutes resolution). */ o
->lru = LRU_CLOCK();return o;
}
/* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain * string object where o->ptr points to a proper sds string.
*/
robj
*createRawStringObject(const char *ptr,>return createObject(OBJ_STRING,sdsnewlen(ptr,len));
}
/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is * an object where the sds string is actually an unmodifiable string
* allocated in the same chunk as the object itself.
*/
robj
*createEmbeddedStringObject(const char *ptr,>robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);struct sdshdr8 *sh = (void*)(o+1);
o
->type = OBJ_STRING; o
->encoding = OBJ_ENCODING_EMBSTR; o
->ptr = sh+1; o
->refcount = 1; o
->lru = LRU_CLOCK();
sh
->len = len; sh
->alloc = len; sh
->flags = SDS_TYPE_8;if (ptr) { memcpy(sh
->buf,ptr,len); sh
->buf[len] = '\0'; }
else { memset(sh
->buf,0,len+1); }
return o;
}
/* Create a string object with EMBSTR encoding 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
* we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc.
*/
#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
/* Note: sdshdr5 is never used, we just access the flags byte directly. * However is here to document the layout of type 5 SDS strings.
*/
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
/* Return ASAP if there is enough space left. */ if (avail >= addlen) return s;
len = sdslen(s);
sh = (char*)s-sdsHdrSize(oldtype);
newlen = (len+addlen);
//内存大小分配
if (newlen < SDS_MAX_PREALLOC)
newlen *= 2;
else
newlen += SDS_MAX_PREALLOC;
type = sdsReqType(newlen);
/* Don't use type 5: the user is appending to the string and type 5 is
* not able to remember empty space, so sdsMakeRoomFor() must be called
* at every appending operation. */
if (type == SDS_TYPE_5) type = SDS_TYPE_8;
hdrlen = sdsHdrSize(type);
if (oldtype==type) {
newsh = s_realloc(sh, hdrlen+newlen+1);
if (newsh == NULL) return NULL;
s = (char*)newsh+hdrlen;
} else {
/* Since the header> * and can't use realloc */
newsh = s_malloc(hdrlen+newlen+1);
if (newsh == NULL) return NULL;
memcpy((char*)newsh+hdrlen, s, len+1);
s_free(sh);
s = (char*)newsh+hdrlen;
s[-1] = type;
sdssetlen(s, len);
}
sdssetalloc(s, newlen);
return s;
}
(2)惰性空间释放 当执行字符串长度缩短的操作的时候,SDS并不直接重新分配多出来的字节,而是修改len和free的值(len相应减小,free相应增大,buf的空间大小不变化)。通过惰性空间释放,可以很好的避免缩短字符串需要的内存重分配的情况。而且多余的空间也可以为将来可能有的字符串增长的操作做优化。
(3)防止内存溢出
char a[10] = "hello";
strcat(a, " world");
strcpy(a, "hello world");
上面的三句代码,就是C语言的字符串拼接和复制的使用,但是明显出现了缓冲区溢出的问题。字符数组a的长度是10,而”hello world”字符串的长度为11,则需要12个字节的空间来存储(不要忘记了’\0’)。Redis的SDS是怎么处理字符串修改的这种情况。当使用SDS的API对字符串进行修改的时候,API内部第一步会检测字符串的大小是否满足。如果空间已经满足要求,那么就像C语言一样操作即可。如果不满足,则拓展buf的空间,使得满足操作的需求,之后再进行操作。每次操作之后,len和free的值会做相应的修改。 总结: SDS 具有以下优点
(1) 常数复杂度获取字符串长度。
(2)杜绝缓冲区溢出。
(3)减少修改字符串长度时所需的内存重分配次数。
(4)二进制安全。
(5)兼容部分 C 字符串函数。 二、命令操作 1. SET SETEX PSETEX SETNX
SET key value [EX][PX][NX][XX]
EX second:设置键的过期时间,单位为秒。(SET key value EX second等同于SETEX key second value)。
PX millisecond:设置键的过期时间,单位为毫秒。(SET key value PX millisecond等同于PSETEX key millisecond value)。
NX:当建不存在时,才对键进行设置操作。(SET key value NX等同于SETNX key value)。
XX:只有键已经存在时,才对键进行设置操作。
(1)对不存在的键进行设置
127.0.0.1:6379> set key "value"
OK
127.0.0.1:6379> get key
"value"
(2)对已存在的键进行设置
127.0.0.1:6379> set key "newvalue"
OK
127.0.0.1:6379> get key
"newvalue"
(3)使用EX选项
127.0.0.1:6379> set key-with-EX "hello" EX 10
OK
127.0.0.1:6379> get key-with-EX
"hello"
127.0.0.1:6379> get key-with-EX
(nil)
(4)使用PX选项
127.0.0.1:6379> set key-with-PX "hello" PX 10000
OK
127.0.0.1:6379> get key-with-PX
"hello"
127.0.0.1:6379> get key-with-PX
(nil)
(5)使用NX|XX选项
127.0.0.1:6379> set key-with-NX "hello" NX
OK
127.0.0.1:6379> set key-with-NX "hello" NX
(nil)
127.0.0.1:6379> set key-with-XX "hello"
OK
127.0.0.1:6379> del key-with-XX
(integer)
1
127.0.0.1:6379> set key-with-XX "hello" XX
(nil)
2. APPEND
APPEND key value
如果key已存在并且是一个字符串,APPEND命令将value追加到key原来的位置。
如果key不存在,APPEND简单的将给定的key设为value。