Redis源码之String操作
0.前言
String操作是Redis操作中最基本的类型,包含get,set,mget,mset,append等等。下面我们会具体分析下一些命令的详细流程,特么简单的命令没有列出。
1.SET命令
2.GET命令
3.SETBIT命令
4.GETBIT命令
5.BTICOUNT命令
6.BTIPOS命令
7.BITOP命令
1.SET命令
set操作set key value [nx, xx, ex, px], 解析完命令参数后,直接调用setCommand进行相应操作
void setCommand(redisClient *c) {
int j;
robj *expire = NULL;
int unit = UNIT_SECONDS;
int flags = REDIS_SET_NO_FLAGS;
/*set key value 占了三个参数,因此从第四个参数开始解析set选项nx,xx,ex,px*/
for (j = 3; j < c->argc; j++) {
char *a = c->argv[j]->ptr;
robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];
/*解析NX,XX,EX,PX标记*/
if ((a[0] == 'n' || a[0] == 'N') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0') {
flags |= REDIS_SET_NX;
} else if ((a[0] == 'x' || a[0] == 'X') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0') {
flags |= REDIS_SET_XX;
} else if ((a[0] == 'e' || a[0] == 'E') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && next) {
unit = UNIT_SECONDS;
expire = next;
j++;
} else if ((a[0] == 'p' || a[0] == 'P') &&
(a[1] == 'x' || a[1] == 'X') && a[2] == '\0' && next) {
unit = UNIT_MILLISECONDS;
expire = next;
j++;
} else {
addReply(c,shared.syntaxerr);
return;
}
}
/*尝试编码字符串,为了节省内存使用共享值或者存储为long类型,可参考《数据结构object》一文*/
c->argv[2] = tryObjectEncoding(c->argv[2]);
/*真正执行set操作函数,下面介绍此函数*/
setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);
}
void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
long long milliseconds = 0; /* initialized to avoid any harmness warning */
if (expire) {
/*将object值取出保存到 milliseconds变量中*/
if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
return;
if (milliseconds <= 0) {
addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name);
return;
}
if (unit == UNIT_SECONDS) milliseconds *= 1000;
}
/*通过查找key值,验证是否符合nx和xx标记,不符合条件直接返回nil*/
if ((flags & REDIS_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||
(flags & REDIS_SET_XX && lookupKeyWrite(c->db,key) == NULL))
{
addReply(c, abort_reply ? abort_reply : shared.nullbulk);
return;
}
/*设置key,value值,下面进行介绍*/
setKey(c->db,key,val);
server.dirty++;
/*如果设置了生存周期,则设置或更新生存周期*/
if (expire) setExpire(c->db,key,mstime()+milliseconds);
/*向订阅键值发生变化事件的客户端,发送事件通知*/
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",key,c->db->id);
/*向订阅命令事件的客户端,发送事件通知*/
if (expire) notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,
"expire",key,c->db->id);
addReply(c, ok_reply ? ok_reply : shared.ok);
}
void setKey(redisDb *db, robj *key, robj *val) {
/*判断是否已经存在key,存在则覆盖,不存在则添加,放入db->dict字典中*/
if (lookupKeyWrite(db,key) == NULL) {
dbAdd(db,key,val);
} else {
dbOverwrite(db,key,val);
}
/*增加引用*/
incrRefCount(val);
/*重置生存周期*/
removeExpire(db,key);
/*如果客户端watch此key,则设置标记,打断当前正在执行的事务,后面客户端执行exec时,则将执行失败*/
signalModifiedKey(db,key);
}
2.GET命令
get操作get key value, 实在是太简单了,直接从db->dict中通过key取出value值,如果没有找到则返回nil。由于Redis带有生存周期key并不会在生命周期到时立即删除,
get时首先会判断生存时间是否到期,到期则直接删除,并同步给slave,返回给客户端nil。
3.SETBIT命令
setbit操作setbit key bitoffset bitval, setbit其实就是对一段内存指定的偏移量上设置一个bit位,直接调用setbitCommand函数进行处理。
void setbitCommand(redisClient *c) {
robj *o;
char *err = "bit is not an integer or out of range";
size_t bitoffset;
int byte, bit;
int byteval, bitval;
long on;
/*bit偏移量,偏移量小于512M,取值时进行了判断*/
if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
return;
/*设置值0或1*/
if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)
return;
/* 判断值有效性,只能0或1 */
if (on & ~1) {
addReplyError(c,err);
return;
}
o = lookupKeyWrite(c->db,c->argv[1]);
if (o == NULL) {
o = createObject(REDIS_STRING,sdsempty());
dbAdd(c->db,c->argv[1],o);
} else {
if (checkType(c,o,REDIS_STRING)) return;
/*保证object独占的非共享,判断如果是共享的string,则重新创建新的string覆盖原来的string */
o = dbUnshareStringValue(c->db,c->argv[1],o);
}
/*计算偏移位置,右移三位相当于对8取整*/
byte = bitoffset >> 3;
/*判断字符串长度是否足够,不够则增加长度并置为0*/
o->ptr = sdsgrowzero(o->ptr,byte+1);
/*首先取出原来位置一个字节保存的值*/
byteval = ((uint8_t*)o->ptr)[byte];
/*bit代表一个字节内从右向左的偏移量*/
bit = 7 - (bitoffset & 0x7);
/*取出原来bit位上值,后面返回给客户端*/
bitval = byteval & (1 << bit);
/*取出此字节上其余7bit位值*/
byteval &= ~(1 << bit);
/*更新客户端需要更新的bit位的值*/
byteval |= ((on & 0x1) << bit);
((uint8_t*)o->ptr)[byte] = byteval;
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
server.dirty++;
addReply(c, bitval ? shared.cone : shared.czero);
}
4.GETBIT命令
getbit操作getbit key bitoffset, 比较简单,直接从一块内存上指定的偏移量上取出bit位值
void getbitCommand(redisClient *c) {
robj *o;
char llbuf[32];
size_t bitoffset;
size_t byte, bit;
size_t bitval = 0;
if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_STRING)) return;
/*计算bit位偏移位置*/
byte = bitoffset >> 3;
bit = 7 - (bitoffset & 0x7);
if (o->encoding != REDIS_ENCODING_RAW) {
/*非二进制编码,就是REDIS_ENCODING_INT编码,转码为字符串取值*/
if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
bitval = llbuf[byte] & (1 << bit);
} else {
/*二进制编码直接取值*/
if (byte < sdslen(o->ptr))
bitval = ((uint8_t*)o->ptr)[byte] & (1 << bit);
}
addReply(c, bitval ? shared.cone : shared.czero);
}
5.BITCOUNT命令
bitcount操作bitcount key [start, end], 统计start到end二进制数据中,start和end的单位是字节,bit为1的个数,使用了几种算法,调用bitcountCommand函数进行处理。
void bitcountCommand(redisClient *c) {
robj *o;
long start, end, strlen;
unsigned char *p;
char llbuf[32];
/* 通过key值查找字符串 */
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_STRING)) return;
/* 获取字符串长度, 如果编码是REDIS_ENCODING_INT, 转换为临时字符串*/
if (o->encoding == REDIS_ENCODING_INT) {
p = (unsigned char*) llbuf;
strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
} else {
p = (unsigned char*) o->ptr;
strlen = sdslen(o->ptr);
}
/* 解析用户输入的start和end参数 */
if (c->argc == 4) {
if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
return;
if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
return;
/* Convert negative indexes */
if (start < 0) start = strlen+start;
if (end < 0) end = strlen+end;
if (start < 0) start = 0;
if (end < 0) end = 0;
if (end >= strlen) end = strlen-1;
} else if (c->argc == 2) {
/* The whole string. */
start = 0;
end = strlen-1;
} else {
/* Syntax error. */
addReply(c,shared.syntaxerr);
return;
}
if (start > end) {
addReply(c,shared.czero);
} else {
long bytes = end-start+1;
/*调用redisPopcount函数计算bit位1的个数, 下面介绍*/
addReplyLongLong(c,redisPopcount(p+start,bytes));
}
}
计算bit为1的个数函数, redis采用了查表法和平行算法, 统计bit位为1的个数算法很多也很有趣,可以参考博客
size_t redisPopcount(void *s, long count) {
size_t bits = 0;
unsigned char *p = s;
uint32_t *p4;
/*bit位值存储表,列出了一个字节的所有值*/
static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8};
/* 判断指针的初始位置是否32bit对齐, 如果没有对齐首先计算没有对齐的部分, 保证32bit对齐, 提高后面算法性能 */
while((unsigned long)p & 3 && count) {
/*使用查表法统计bit为1的个数*/
bits += bitsinbyte[*p++];
count--;
}
/* 每次对16个字节进行统计 */
p4 = (uint32_t*)p;
while(count>=16) {
uint32_t aux1, aux2, aux3, aux4;
aux1 = *p4++;
aux2 = *p4++;
aux3 = *p4++;
aux4 = *p4++;
count -= 16;
/*首先计算相邻两个bit位中1的个数*/
aux1 = aux1 - ((aux1 >> 1) & 0x55555555);
/*计算相邻四位bit位中1的个数*/
aux1 = (aux1 & 0x33333333) + ((aux1 >> 2) & 0x33333333);
aux2 = aux2 - ((aux2 >> 1) & 0x55555555);
aux2 = (aux2 & 0x33333333) + ((aux2 >> 2) & 0x33333333);
aux3 = aux3 - ((aux3 >> 1) & 0x55555555);
aux3 = (aux3 & 0x33333333) + ((aux3 >> 2) & 0x33333333);
aux4 = aux4 - ((aux4 >> 1) & 0x55555555);
aux4 = (aux4 & 0x33333333) + ((aux4 >> 2) & 0x33333333);
/*
*(aux1 + (aux1 >> 4))计算一个字节内1的个数, 和0x0F0F0F0F进行与运算提取出每个字节中计算结果, 和0x01010101相乘相当于对每个字节进行加和,
*结果保存在最高一个字节中, 右移24位, 取出计算结果.
*/
bits += ((((aux1 + (aux1 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) +
((((aux2 + (aux2 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) +
((((aux3 + (aux3 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) +
((((aux4 + (aux4 >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24);
}
/* 计算剩余数据中1的个数 */
p = (unsigned char*)p4;
while(count--) bits += bitsinbyte[*p++];
return bits;
}
6.BITPOS命令
bitpos命令bitpos bit [start, end], 查找一段二进制中start到end中0或者1第一次出现的位置,start和end单位是字节,调用bitposCommand函数处理。
void bitposCommand(redisClient *c) {
robj *o;
long bit, start, end, strlen;
unsigned char *p;
char llbuf[32];
int end_given = 0;
/* 获取查找的bit位值,0或者1*/
if (getLongFromObjectOrReply(c,c->argv[2],&bit,NULL) != REDIS_OK)
return;
if (bit != 0 && bit != 1) {
addReplyError(c, "The bit argument must be 1 or 0.");
return;
}
/* 如果key不存在,查找bit为0,则返回0 ,否则返回-1*/
if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
addReplyLongLong(c, bit ? -1 : 0);
return;
}
if (checkType(c,o,REDIS_STRING)) return;
/* 获取字符串长度, 如果编码是REDIS_ENCODING_INT, 转换为临时字符串*/
if (o->encoding == REDIS_ENCODING_INT) {
p = (unsigned char*) llbuf;
strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
} else {
p = (unsigned char*) o->ptr;
strlen = sdslen(o->ptr);
}
/* 解析用户输入的start和end参数,参数是字节为单位*/
if (c->argc == 4 || c->argc == 5) {
if (getLongFromObjectOrReply(c,c->argv[3],&start,NULL) != REDIS_OK)
return;
if (c->argc == 5) {
if (getLongFromObjectOrReply(c,c->argv[4],&end,NULL) != REDIS_OK)
return;
end_given = 1;
} else {
end = strlen-1;
}
/* Convert negative indexes */
if (start < 0) start = strlen+start;
if (end < 0) end = strlen+end;
if (start < 0) start = 0;
if (end < 0) end = 0;
if (end >= strlen) end = strlen-1;
} else if (c->argc == 3) {
/* The whole string. */
start = 0;
end = strlen-1;
} else {
/* Syntax error. */
addReply(c,shared.syntaxerr);
return;
}
if (start > end) {
addReplyLongLong(c, -1);
} else {
long bytes = end-start+1;
/*调用redisBitpos函数计算bit位位置,下面介绍*/
long pos = redisBitpos(p+start,bytes,bit);
if (end_given && bit == 0 && pos == bytes*8) {
addReplyLongLong(c,-1);
return;
}
/*最后结果要加上先前用户给出的start开始长度*/
if (pos != -1) pos += start*8; /* Adjust for the bytes we skipped. */
addReplyLongLong(c,pos);
}
}
long redisBitpos(void *s, unsigned long count, int bit) {
unsigned long *l;
unsigned char *c;
unsigned long skipval, word = 0, one;
long pos = 0; /* Position of bit, to return to the caller. */
unsigned long j;
/*
*首先判断字节是否按sizeof(*l)对齐,不对齐则一个一个字节进行比较,直到sizeof(*l)对齐或者是count为0
*/
skipval = bit ? 0 : UCHAR_MAX;
c = (unsigned char*) s;
while((unsigned long)c & (sizeof(*l)-1) && count) {
if (*c != skipval) break;
c++;
count--;
pos += 8;
}
/*
*如果字节对齐时,count值没有为0,则按照sizeof(*l)长度进行查找,加快查找速度,特别是对大块的连续0或者1
*/
skipval = bit ? 0 : ULONG_MAX;
l = (unsigned long*) c;
while (count >= sizeof(*l)) {
if (*l != skipval) break;
l++;
count -= sizeof(*l);
pos += sizeof(*l)*8;
}
/*查找结束后,将查找最后的到值以大端形式写入word,并不一定找到*/
c = (unsigned char*)l;
for (j = 0; j < sizeof(*l); j++) {
word <<= 8;
if (count) {
word |= *c;
c++;
count--;
}
}
/*bit为1时,没有找到直接返回-1*/
if (bit == 1 && word == 0) return -1;
/*
*设置one最高位位1,其他位为0, 用于取出word中每一位
*/
one = ULONG_MAX; /* All bits set to 1.*/
one >>= 1; /* All bits set to 1 but the MSB. */
one = ~one; /* All bits set to 0 but the MSB. */
/*
*依次取出word中每一位进行比较,直到找到bit
*/
while(one) {
/*这里比较设计的很巧妙,节省了一个变量记录移动位数*/
if (((one & word) != 0) == bit) return pos;
pos++;
one >>= 1;
}
/*
*这里并不会到达,上面已经把所有情况考虑到了
*/
redisPanic("End of redisBitpos() reached.");
return 0; /* Just to avoid warnings. */
}
7.BITOP命令
bitop操作bitop operation destkey key [key ...], operation可以是and, or, xor, not,对后面列出的key二进制数据之间进行operation运算,结果保存在destkey中,操作时间复杂度O(N),数据量过大可能会非常耗时,调用bitopCommand函数处理。
void bitopCommand(redisClient *c) {
char *opname = c->argv[1]->ptr;
robj *o, *targetkey = c->argv[2];
unsigned long op, j, numkeys;
robj **objects; /* Array of source objects. */
unsigned char **src; /* Array of source strings pointers. */
unsigned long *len, maxlen = 0; /* Array of length of src strings,
and max len. */
unsigned long minlen = 0; /* Min len among the input keys. */
unsigned char *res = NULL; /* Resulting string. */
/* 解析运算类型,与,或,异或,非*/
if ((opname[0] == 'a' || opname[0] == 'A') && !strcasecmp(opname,"and"))
op = BITOP_AND;
else if((opname[0] == 'o' || opname[0] == 'O') && !strcasecmp(opname,"or"))
op = BITOP_OR;
else if((opname[0] == 'x' || opname[0] == 'X') && !strcasecmp(opname,"xor"))
op = BITOP_XOR;
else if((opname[0] == 'n' || opname[0] == 'N') && !strcasecmp(opname,"not"))
op = BITOP_NOT;
else {
addReply(c,shared.syntaxerr);
return;
}
/* 操作为非时,key只能有一个*/
if (op == BITOP_NOT && c->argc != 4) {
addReplyError(c,"BITOP NOT must be called with a single source key.");
return;
}
/* 计算key数量,然后为所有key分配一个数组,后面运算时使用 */
numkeys = c->argc - 3;
src = zmalloc(sizeof(unsigned char*) * numkeys);
len = zmalloc(sizeof(long) * numkeys);
objects = zmalloc(sizeof(robj*) * numkeys);
for (j = 0; j < numkeys; j++) {
o = lookupKeyRead(c->db,c->argv[j+3]);
/* 如果找不到key对应的值,则赋值空字符串*/
if (o == NULL) {
objects[j] = NULL;
src[j] = NULL;
len[j] = 0;
minlen = 0;
continue;
}
/* 参与运行的key对应值,必须为字符串,非字符串直接返回错误 */
if (checkType(c,o,REDIS_STRING)) {
unsigned long i;
for (i = 0; i < j; i++) {
if (objects[i])
decrRefCount(objects[i]);
}
zfree(src);
zfree(len);
zfree(objects);
return;
}
objects[j] = getDecodedObject(o);
src[j] = objects[j]->ptr;
len[j] = sdslen(objects[j]->ptr);
/*利用循环直接计算出key对应的字符串最大长度和最小长度*/
if (len[j] > maxlen) maxlen = len[j];
if (j == 0 || len[j] < minlen) minlen = len[j];
}
/* 至少有一个string非空,才进行计算操作 */
if (maxlen) {
res = (unsigned char*) sdsnewlen(NULL,maxlen);
unsigned char output, byte;
unsigned long i;
/* 如果key少于16个,为什么是16可能是经验值,就执行下面算法,算法多了一次copy操作,但循环次数变少了,一次循环运算4*sizeof(unsigned long)个字节*/
j = 0;
if (minlen && numkeys <= 16) {
unsigned long *lp[16];
unsigned long *lres = (unsigned long*) res;
/*sds字符串本身8字节对齐,不用在进行对齐操作*/
memcpy(lp,src,sizeof(unsigned long*)*numkeys);
/*copy第一个key中value值,以备后面进行运算*/
memcpy(res,src[0],minlen);
/* 根据不同操作进行相应计算,每次运算4*sizeof(unsigned long)字节 */
if (op == BITOP_AND) {
while(minlen >= sizeof(unsigned long)*4) {
for (i = 1; i < numkeys; i++) {
lres[0] &= lp[i][0];
lres[1] &= lp[i][1];
lres[2] &= lp[i][2];
lres[3] &= lp[i][3];
lp[i]+=4;
}
lres+=4;
j += sizeof(unsigned long)*4;
minlen -= sizeof(unsigned long)*4;
}
} else if (op == BITOP_OR) {
while(minlen >= sizeof(unsigned long)*4) {
for (i = 1; i < numkeys; i++) {
lres[0] |= lp[i][0];
lres[1] |= lp[i][1];
lres[2] |= lp[i][2];
lres[3] |= lp[i][3];
lp[i]+=4;
}
lres+=4;
j += sizeof(unsigned long)*4;
minlen -= sizeof(unsigned long)*4;
}
} else if (op == BITOP_XOR) {
while(minlen >= sizeof(unsigned long)*4) {
for (i = 1; i < numkeys; i++) {
lres[0] ^= lp[i][0];
lres[1] ^= lp[i][1];
lres[2] ^= lp[i][2];
lres[3] ^= lp[i][3];
lp[i]+=4;
}
lres+=4;
j += sizeof(unsigned long)*4;
minlen -= sizeof(unsigned long)*4;
}
} else if (op == BITOP_NOT) {
while(minlen >= sizeof(unsigned long)*4) {
lres[0] = ~lres[0];
lres[1] = ~lres[1];
lres[2] = ~lres[2];
lres[3] = ~lres[3];
lres+=4;
j += sizeof(unsigned long)*4;
minlen -= sizeof(unsigned long)*4;
}
}
}
/* 这里既处理上面算法中剩余的字节,也处理key数量大于16时,使用的是最原始的方式一个字节一个字节进行运算 */
for (; j < maxlen; j++) {
output = (len[0] <= j) ? 0 : src[0][j];
if (op == BITOP_NOT) output = ~output;
for (i = 1; i < numkeys; i++) {
byte = (len[i] <= j) ? 0 : src[i][j];
switch(op) {
case BITOP_AND: output &= byte; break;
case BITOP_OR: output |= byte; break;
case BITOP_XOR: output ^= byte; break;
}
}
res[j] = output;
}
}
for (j = 0; j < numkeys; j++) {
if (objects[j])
decrRefCount(objects[j]);
}
zfree(src);
zfree(len);
zfree(objects);
/* 如果至少一个非空,则将结果存入targetkey中,否则在targetkey存在情况下执行删除操作 */
if (maxlen) {
o = createObject(REDIS_STRING,res);
setKey(c->db,targetkey,o);
notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",targetkey,c->db->id);
decrRefCount(o);
} else if (dbDelete(c->db,targetkey)) {
signalModifiedKey(c->db,targetkey);
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",targetkey,c->db->id);
}
server.dirty++;
/*返回给客户端字符串最大长度*/
addReplyLongLong(c,maxlen);
}
Redis源码之String操作的更多相关文章
- Redis源码分析系列
0.前言 Redis目前热门NoSQL内存数据库,代码量不是很大,本系列是本人阅读Redis源码时记录的笔记,由于时间仓促和水平有限,文中难免会有错误之处,欢迎读者指出,共同学习进步,本文使用的Red ...
- Redis源码阅读笔记(1)——简单动态字符串sds实现原理
首先,sds即simple dynamic string,redis实现这个的时候使用了一个技巧,并且C99将其收录为标准,即柔性数组成员(flexible array member),参考资料见这里 ...
- redis源码笔记(一) —— 从redis的启动到command的分发
本作品采用知识共享署名 4.0 国际许可协议进行许可.转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/redis1 本博客同步在http://www.cnblog ...
- Redis源码学习:字符串
Redis源码学习:字符串 1.初识SDS 1.1 SDS定义 Redis定义了一个叫做sdshdr(SDS or simple dynamic string)的数据结构.SDS不仅用于 保存字符串, ...
- Redis源码学习:Lua脚本
Redis源码学习:Lua脚本 1.Sublime Text配置 我是在Win7下,用Sublime Text + Cygwin开发的,配置方法请参考<Sublime Text 3下C/C++开 ...
- 玩一把redis源码(一):为redis添加自己的列表类型
2019年第一篇文档,为2019年做个良好的开端,本文档通过step by step的方式向读者展示如何为redis添加一个数据类型,阅读本文档后读者对redis源码的执行逻辑会有比较清晰的认识,并且 ...
- redis源码(一):为redis添加自己的列表类型
本文档分为三大部分: 环境介绍与效果演示 redis接收命令到返回数据的执行逻辑 代码实现 文档的重点和难点在第三部分,完全阅读本文档需要读者具备基本的c语言和数据结构知识. 环境介绍和效果演示环境介 ...
- 曹工说Redis源码(3)-- redis server 启动过程完整解析(中)
文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...
- Redis源码阅读一:简单动态字符串SDS
源码阅读基于Redis4.0.9 SDS介绍 redis 127.0.0.1:6379> SET dbname redis OK redis 127.0.0.1:6379> GET dbn ...
随机推荐
- LCA+差分【CF191C】Fools and Roads
Description 有一颗 \(n\) 个节点的树,\(k\) 次旅行,问每一条边被走过的次数. Input 第一行一个整数 \(n\) (\(2\leq n\leq 10^5\)). 接下来 \ ...
- Codeforces Round #283 (Div. 2) D. Tennis Game(模拟)
D. Tennis Game time limit per test 2 seconds memory limit per test 256 megabytes input standard inpu ...
- 22、Flask实战第22天:Flask信号
Flask中的信号使用的是一个第三方插件blinker.通过pip list看一下是否安装,如果没有,则使用如下命令安装 pip install blinker 自定义信号 自定义信号分为3步: ①定 ...
- 【DFS】【最短路】【spfa】【BFS】洛谷P2296 NOIP2014提高组 day2 T2 寻找道路
存反图,从终点dfs一遍,记录下无法到达的点. 然后枚举这些记录的点,把他们的出边所连的点也全部记录. 以上这些点都是无法在最短路中出现的. 所以把两个端点都没被记录的边加进图里,跑spfa.BFS什 ...
- 使用MultipleInputs和MultipleOutputs
还是计算矩阵的乘积,待计算的表达式如下: S=F*[B+mu(u+s+b+d)] 其中,矩阵B.u.s.d分别存放在名称对应的SequenceFile文件中. 1)我们想分别读取这些文件(放在不同的文 ...
- 【重装系统】老毛桃U盘工具V2013超级装机版-安装原版Win7/Win8
老毛桃U盘工具V2013超级装机版-程序下载和运行 老毛桃U盘工具V2013超级装机版-安装原版XP的方法 老毛桃U盘工具V2013超级装机版-安装原版Win7/Win8
- 破解SQLServer for Linux预览版的3.5GB内存限制 (RHEL篇) 转
https://www.ancii.com/database/30842.html 微软发布了SQLServer for Linux,但是安装竟然需要3.5GB内存,这让大部分云主机用户都没办法尝试这 ...
- EF 通用数据层父类方法小结
MSSql 数据库 数据层 父类 增删改查: using System;using System.Collections.Generic;using System.Data;using System. ...
- [转]SVN安装问题The Apache Portable Runtime (APR) library cannot be found
http://blog.csdn.net/ckwer2008/article/details/47972601 Linux很多地方编译的时候都会用到apr 如果找不到apr就会报错 configure ...
- 2017年最全的30个Android面试题,你将如何回答?
百度首页 登录 2017年最全的30个Android面试题,你将如何回答? 机翼技术 百家号 03-10 02:32 “三金四银”又是一年一度的跳槽季,相信有不少Android程序员开始摩拳擦掌蠢蠢欲 ...