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操作的更多相关文章

  1. Redis源码分析系列

    0.前言 Redis目前热门NoSQL内存数据库,代码量不是很大,本系列是本人阅读Redis源码时记录的笔记,由于时间仓促和水平有限,文中难免会有错误之处,欢迎读者指出,共同学习进步,本文使用的Red ...

  2. Redis源码阅读笔记(1)——简单动态字符串sds实现原理

    首先,sds即simple dynamic string,redis实现这个的时候使用了一个技巧,并且C99将其收录为标准,即柔性数组成员(flexible array member),参考资料见这里 ...

  3. redis源码笔记(一) —— 从redis的启动到command的分发

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/redis1 本博客同步在http://www.cnblog ...

  4. Redis源码学习:字符串

    Redis源码学习:字符串 1.初识SDS 1.1 SDS定义 Redis定义了一个叫做sdshdr(SDS or simple dynamic string)的数据结构.SDS不仅用于 保存字符串, ...

  5. Redis源码学习:Lua脚本

    Redis源码学习:Lua脚本 1.Sublime Text配置 我是在Win7下,用Sublime Text + Cygwin开发的,配置方法请参考<Sublime Text 3下C/C++开 ...

  6. 玩一把redis源码(一):为redis添加自己的列表类型

    2019年第一篇文档,为2019年做个良好的开端,本文档通过step by step的方式向读者展示如何为redis添加一个数据类型,阅读本文档后读者对redis源码的执行逻辑会有比较清晰的认识,并且 ...

  7. redis源码(一):为redis添加自己的列表类型

    本文档分为三大部分: 环境介绍与效果演示 redis接收命令到返回数据的执行逻辑 代码实现 文档的重点和难点在第三部分,完全阅读本文档需要读者具备基本的c语言和数据结构知识. 环境介绍和效果演示环境介 ...

  8. 曹工说Redis源码(3)-- redis server 启动过程完整解析(中)

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  9. Redis源码研究--字典

    计划每天花1小时学习Redis 源码.在博客上做个记录. --------6月18日----------- redis的字典dict主要涉及几个数据结构, dictEntry:具体的k-v链表结点 d ...

随机推荐

  1. 《Invert》开发日志05:终止

    今天终于看了久闻大名的<独立游戏大电影>,然后我就做了一个坑爹的决定:终止“Invert”项目的开发.没错,在还没正式开工之前,我就决定停掉这个项目,而且是永久终止.做这个决定并不是因为觉 ...

  2. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(十四)之漏掉的客服消息

    前言 不知不觉已经十四篇了,其实已经没有什么可写了.但是突然发现layim中带的客服功能没有用到.于是乎,抽点时间完成吧.其实之前的工作已经把客服功能完成了一大半,剩下的我们稍微调整即可.今天的演示我 ...

  3. hdu 饭卡

    本题的思路是:首先如果m<5,直接输出:若m>5,则先拿出5元钱买最贵的东西,这样背包容量就变成了m-5,商品数量为n-1的0/1背包问题. 此题的状态转移方程为:dp[j]=max{dp ...

  4. Python 调用自定义包

    创建包 # mkdir -p /python/utils # touch /python/utils/__init__.py # vi /python/utils/Log.pyimport timed ...

  5. mac app icon 设置

    mac app icon 设置 1:目前 mac app 所需要的icon 图标尺寸 icon_16x16.png 16px icon_16x16@2x.png 32px icon_32x32.png ...

  6. mysql之union

    今天来写写union的用法及一些需要注意的. union:联合的意思,即把两次或多次查询结果合并起来. 要求:两次查询的列数必须一致 推荐:列的类型可以不一样,但推荐查询的每一列,想对应的类型以一样 ...

  7. Java并发编程:volatile 关键字

    转自:http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html 其实Java语言是支持多线程的,为了解决线程并发的问题,在语言内部 ...

  8. flask框架----整合Flask中的目录结构

    一.SQLAlchemy-Utils 由于sqlalchemy中没有提供choice方法,所以借助SQLAlchemy-Utils组件提供的choice方法 import datetime from ...

  9. 第一个android ijkplayer播放器

    创建一个ijkplayer的播放器项目,需要三步设置: 一.在activity_main.xml中添加播放器标签 <com.smallart.myapplication.media.IjkVid ...

  10. Vue组件通讯

    Vue最常用的组件通讯有三种:父->子组件通讯.子->父组件通讯,兄弟组件通讯.(template用的pug模板语法) 1.父->子组件通讯 父->子组件通讯,是通过props ...