fansenjun 2019-06-27
顺风车运营研发团队 施洪宝
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 } }