【Redis學習筆記】2018-07-05 Redis指令學習1

順風車運營研發團隊 施洪寶html

getbit, getrange, blpop, brpop, rpopredis

一. getbit
1.命令說明
使用方式: getbit key offsetide

功能: 對key對應value的值, 取對應偏移量上的值。ui

返回值: key不存在, offset比字符串長度大時, 返回0, 不然返回對應位上的值。this

時間複雜度: O(1)spa

2.源碼實現
源碼實現的步驟能夠分爲兩步: 1. 獲取偏移量所在字節數。 2. 獲取偏移量所在字節的bit。code

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...orm

二. getrange
1.命名說明
使用方式:getrange key start endserver

功能: 返回key中字符串值得子字符串,字符串截取位置由start, end決定htm

返回值: 截取的子字符串,超過範圍自動截取

時間複雜度: 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
    }
}

3.參考
http://doc.redisfans.com/list...

相關文章
相關標籤/搜索