顺风车运营研发团队 施洪宝
getbit, getrange, blpop, brpop, rpop
一. getbit
1.命令说明
使用方式: getbit key offset
功能: 对key对应value的值, 取对应偏移量上的值。
返回值: key不存在, offset比字符串长度大时, 返回0, 否则返回对应位上的值。
时间复杂度: O(1)
2.源码实现
源码实现的步骤可以分为两步: 1. 获取偏移量所在字节数。 2. 获取偏移量所在字节的bit。
void getbitCommand(client *c) {
robj *o;
char llbuf[32];
size_t bitoffset;
size_t byte, bit;
size_t bitval = 0;
//读取参数
if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != C_OK)
return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,OBJ_STRING)) return;
//获取offset对应的字节位置以及对应的bit
byte = bitoffset >> 3;
bit = 7 - (bitoffset & 0x7);
//读取结果并返回
if (sdsEncodedObject(o)) {
if (byte < sdslen(o->ptr))
bitval = ((uint8_t*)o->ptr)[byte] & (1 << bit);
} else {
if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
bitval = llbuf[byte] & (1 << bit);
}
addReply(c, bitval ? shared.cone : shared.czero);
}
3. 参考
http://doc.redisfans.com/stri...
二. getrange
1.命名说明
使用方式:getrange key start end
功能: 返回key中字符串值得子字符串,字符串截取位置由start, end决定
返回值: 截取的子字符串,超过范围自动截取
时间复杂度: O(n)
2.源码实现
void getrangeCommand(client *c) {
robj *o;
long long start, end;
char *str, llbuf[32];
size_t strlen;
//获取参数
if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK)
return;
if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK)
return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
checkType(c,o,OBJ_STRING)) return;
//获取值对应的字符串
if (o->encoding == OBJ_ENCODING_INT) {
str = llbuf;
strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
} else {
str = o->ptr;
strlen = sdslen(str);
}
//start, end处理
/* Convert negative indexes */
if (start < 0 && end < 0 && start > end) {
addReply(c,shared.emptybulk);
return;
}
if (start < 0) start = strlen+start;
if (end < 0) end = strlen+end;
if (start < 0) start = 0;
if (end < 0) end = 0;
if ((unsigned long long)end >= strlen) end = strlen-1;
/* Precondition: end >= 0 && end < strlen, so the only condition where
* nothing can be returned is: start > end. */
if (start > end || strlen == 0) {
addReply(c,shared.emptybulk);
} else {
addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
}
}
3.参考
http://doc.redisfans.com/stri...
三. blpop
1.命令说明
使用方法: blpop key [key...] timeout
功能: 从给定的key中,找到一个非空列表,返回头元素。查找顺序按照给定的key顺序。如果所有key对应的列表都是空的,则操作被阻塞,阻塞时间可由timeout设置
时间复杂度: O(n), n为key的个数(没有被阻塞的情况下)
返回值: 返回key以及列表的元素
2.源码实现
void blockingPopGenericCommand(client *c, int where) {
robj *o;
mstime_t timeout;
int j;
//获取超时时间
if (getTimeoutFromObjectOrReply(c,c->argv[c->argc-1],&timeout,UNIT_SECONDS)
!= C_OK) return;
//依次遍历所有给定的key
for (j = 1; j < c->argc-1; j++) {
//获取key对应的value
o = lookupKeyWrite(c->db,c->argv[j]);
if (o != NULL) {
if (o->type != OBJ_LIST) {
addReply(c,shared.wrongtypeerr);
return;
} else {
if (listTypeLength(o) != 0) {
/* Non empty list, this is like a non normal [LR]POP. */
char *event = (where == LIST_HEAD) ? "lpop" : "rpop";
robj *value = listTypePop(o,where); //从列表中弹出一个元素,位置由where决定, 头部或者尾部。
serverAssert(value != NULL);
//返回结果
addReplyMultiBulkLen(c,2);
addReplyBulk(c,c->argv[j]);
addReplyBulk(c,value);
decrRefCount(value);
//发送键空间事件通知(pub, sub)
notifyKeyspaceEvent(NOTIFY_LIST,event,
c->argv[j],c->db->id);
if (listTypeLength(o) == 0) {
dbDelete(c->db,c->argv[j]);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",
c->argv[j],c->db->id);
}
//通知正在watch这个key的client, 具体实现为将client的flags开启CLIENT_DIRTY_CAS
signalModifiedKey(c->db,c->argv[j]);
server.dirty++;
/* Replicate it as an [LR]POP instead of B[LR]POP. */
//因为已经在一个key上找到元素,故而可以将blpop, brpop改写为lpop, rpop
rewriteClientCommandVector(c,2,
(where == LIST_HEAD) ? shared.lpop : shared.rpop,
c->argv[j]);
return;
}
}
}
}
/* If we are inside a MULTI/EXEC and the list is empty the only thing
* we can do is treating it as a timeout (even with timeout 0). */
//Redis正在执行事务
if (c->flags & CLIENT_MULTI) {
addReply(c,shared.nullmultibulk);
return;
}
/* If the list is empty or the key does not exists we must block */
//列表为空或者key不存在
blockForKeys(c, c->argv + 1, c->argc - 2, timeout, NULL);
}
3. 参考
http://doc.redisfans.com/list...
四. brpop
1.命令说明
使用方法: rlpop key [key...] timeout
功能: 从给定的key中,找到一个非空列表,返回尾部元素。查找顺序按照给定的key顺序。如果所有key对应的列表都是空的,则操作被阻塞,阻塞时间可由timeout设置
时间复杂度: O(n), n为key的个数(没有被阻塞的情况下)
返回值: 返回key以及列表的元素
2.源码实现
与blpop相同,二者都是通过调用blockingPopGenericCommand来实现的,唯一的区别再与,blpop弹出首部(blockingPopGenericCommand的where参数为LIST_HEAD),brpop弹出尾部元素(blockingPopGenericCommand的where参数为LIST_TAIL)
3. 参考
http://doc.redisfans.com/list...
五. rpop
1.命令说明
使用方法: rpop key
功能: 移除并返回列表的尾部元素。
返回值: 返回列表的尾部元素,不存在时返回nil
时间复杂度: O(1)
2. 源码实现
void popGenericCommand(client *c, int where) {
robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk); //查找key
if (o == NULL || checkType(c,o,OBJ_LIST)) return;
robj *value = listTypePop(o,where);
if (value == NULL) {
addReply(c,shared.nullbulk); //value中没有元素
} else {
char *event = (where == LIST_HEAD) ? "lpop" : "rpop";
addReplyBulk(c,value); //返回结果
decrRefCount(value);
notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id); //键空间通知
if (listTypeLength(o) == 0) {
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",
c->argv[1],c->db->id);
dbDelete(c->db,c->argv[1]);
}
signalModifiedKey(c->db,c->argv[1]); //通知正在监听这个key的client, 具体实现为将client的flags开启CLIENT_DIRTY_CAS
server.dirty++; //脏标志加1
}
}