当前位置:网站首页>Redis source code and design analysis -- 13. Ordered collection objects

Redis source code and design analysis -- 13. Ordered collection objects

2022-07-23 10:50:00 JunesFour

Redis An orderly collection of objects



1. The structure of an ordered collection object

The encoding of an ordered set can be ziplist perhaps skiplist.

When using ziplist When coding , Each collection element uses two packed list nodes next to each other to hold , The first node holds the members of the element , And the second element holds the score of the element , The set elements in the compressed list are sorted from small to large . Elements with small scores are close to the header , Elements with high scores are near the end of the table . Here's the picture :

 Insert picture description here
 Insert picture description here

When using skiplist When coding , Of ordered collection objects ptr The pointer will point to a zset structure , This structure contains both a dictionary and a jump table :

//  Ordered set type 
typedef struct zset {
    
    dict *dict;
    zskiplist *zsl;
} zset;

Pictured :

 Insert picture description here

But why use two data structures , Instead of using jump tables or dictionaries directly ?

When you want to find the score of an ordered set object according to its members , Dictionary will be used to realize , The time complexity is O(1), And if you just use the jump table , This complexity will be O(log(n)).

When an ordered collection object wants to perform a range operation , such as ZRANKZRANGE When ordered , We will use jump table to realize , And if we only use dictionary to realize , Each time, the found elements should be reordered , The time complexity of sorting is at least O(NlogN), And the extra O(N) Spatial complexity ( Save sorting results ).

therefore Redis We choose to use dictionary and jump table to realize ordered set . But these two data structures share the members and scores of the same element through pointers , So using jump table and dictionary to save set elements at the same time will not produce any duplicate members or scores , It will not waste extra space .

You can refer to the following structure diagram , For the convenience of display , The members and scores of each element are repeatedly displayed in dictionaries and jump tables , But in practice , Dictionaries and jump tables share members and scores of elements , So it doesn't cause any data duplication , It will not waste any space .

 Insert picture description here

2. Ordered collection object encoding

2.1 Coding rules

When an ordered collection object can satisfy both of the following conditions , Object use ziplist code :

  • The ordered set holds less elements than 128 individual .
  • The length of all element members in an ordered set is less than 64 byte .

An ordered collection object that does not satisfy both of the above conditions will use skiplist code .

2.2 Encoding conversion

For the use of ziplist Coded ordered sets of objects , When using ziplist When either of the two conditions required for encoding cannot be satisfied , The object's transcoding operation is performed , All collection elements originally saved in the compressed list are transferred and saved to the zset Inside the structure , The object's encoding will also be from ziplist Turn into skiplist.

3. Introduction to the order collection object command

command describe
ZADD key score1 member1 [score2 member2] Add one or more members... To an ordered collection , Or update scores of existing members
ZCARD key Get the number of members of the ordered set
ZCOUNT key min max Calculates the number of members of a specified interval fraction in an ordered set
ZINCRBY key increment member Add the increment... To the score of the specified member in the ordered set increment
ZINTERSTORE destination numkeys key [key …] Calculates the intersection of a given ordered set or sets and stores the result set in a new ordered set key in
ZLEXCOUNT key min max Computes the number of members in the specified dictionary interval in an ordered collection
ZRANGE key start stop [WITHSCORES] Returns an ordered set through an index interval to synthesize members in a specified interval
ZRANGEBYLEX key min max [LIMIT offset count] Returns the members of an ordered set through a dictionary interval
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] Returns the members of an ordered set in a specified interval through scores
ZRANK key member Returns the index of a specified member in an ordered collection
ZREM key member [member …] Remove one or more members of an ordered collection
ZREMRANGEBYLEX key min max Remove all members of a given dictionary interval from an ordered set
ZREMRANGEBYRANK key start stop Remove all members of a given rank range from an ordered set
ZREMRANGEBYSCORE key min max Remove all members of a given fraction interval from an ordered set
ZREVRANGE key start stop [WITHSCORES] Returns the members of a specified interval in an ordered set , Through the index , Score from high to the end
ZREVRANGEBYSCORE key max min [WITHSCORES] Returns the members of the specified score range in an ordered set , Rank scores from high to low
ZREVRANK key member Returns the rank of a specified member in an ordered set , Members of an ordered set are decremented by fractions ( From big to small ) Sort
ZSCORE key member Back to the ordered set , The score of a member
ZUNIONSTORE destination numkeys key [key …] Computes the union of a given ordered set or sets , And store it in the new key in
ZSCAN key cursor [MATCH pattern] [COUNT count] Iterate over elements in an ordered set ( Include element members and element scores )

4. Implementation of the order collection object command

The source code of ordered collection objects is server.h Header files and t_zset.c In the source file .

4.1 How to limit the scope of an ordered set

We can find that ordered sets have three main commands about range operations :

command describe
ZRANGE key start stop [WITHSCORES] Returns an ordered set through an index interval to synthesize members in a specified interval
ZRANGEBYLEX key min max [LIMIT offset count] Returns the members of an ordered set through a dictionary interval
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] Returns the members of an ordered set in a specified interval through scores

therefore Redis Ordered sets have three kinds of scope restrictions :

  • According to the dictionary interval
  • According to the ranking ( Indexes ) Section
  • According to the score range

The above three mainly rely on two sorting methods :

  • Score sequence
  • Dictionary order

The range represented by the above two sorts , Depends on two structures , A specified range and boundary of score order , A scope and boundary that defines the lexicographic order :

The following structures are defined in server.h Header file .

/* Struct to hold a inclusive/exclusive range spec by score comparison. */
typedef struct {
    
	//  Min and Max 
    double min, max;

    // minex Indicates whether the minimum value is included in this range ,1 Does not include ,0 Means to contain 
    //  Empathy maxex Represents the maximum value 
    int minex, maxex; /* are min or max exclusive? */
} zrangespec;

/* Struct to hold an inclusive/exclusive range spec by lexicographic comparison. */
typedef struct {
    
    robj *min, *max;  /* May be set to shared.(minstring|maxstring) */// Min and Max 

    // minex Indicates whether the minimum value is included in this range ,1 Does not include ,0 Means to contain 
    //  Empathy maxex Represents the maximum value 
    int minex, maxex; /* are min or max exclusive? */
} zlexrangespec;

4.2 Range operation of ordered set

4.2.1 ZRANGE

// ZRANGE key start stop [WITHSCORES]
// ZRANGE Command implementation 
void zrangeCommand(client *c) {
    
    zrangeGenericCommand(c,0);
}

// ZREVRANGE key start stop [WITHSCORES]
// ZREVRANGE Command implementation 
void zrevrangeCommand(client *c) {
    
    zrangeGenericCommand(c,1);
}

Underlying implementation

// ZRANGE key start stop [WITHSCORES]
// ZREVRANGE key start stop [WITHSCORES]
// ZRANGE、ZREVRANGE Command bottom implementation 
void zrangeGenericCommand(client *c, int reverse) {
    
    robj *key = c->argv[1];
    robj *zobj;
    int withscores = 0;
    long start;
    long end;
    int llen;
    int rangelen;

    //  Take out the starting position start And end position end
    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != C_OK) ||
        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != C_OK)) return;

    //  If there is WITHSCORES Parameters , Set logo 
    if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,"withscores")) {
    
        withscores = 1;
    //  Too many parameters send syntax error message 
    } else if (c->argc >= 5) {
    
        addReply(c,shared.syntaxerr);
        return;
    }

    //  Take out with read operation key Order the collection and check the data type of the collection 
    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptymultibulk)) == NULL
         || checkType(c,zobj,OBJ_ZSET)) return;

    /* Sanitize indexes. */
    //  Get the subscript range of the ordered set 
    llen = zsetLength(zobj);
    //  Handle negative Subscripts 
    if (start < 0) start = llen+start;
    if (end < 0) end = llen+end;
    if (start < 0) start = 0;

    /* Invariant: start >= 0, so this test will be true when end < 0. * The range is empty when start > end or start >= length. */
    //  Trim adjust subscript 
    if (start > end || start >= llen) {
    
        addReply(c,shared.emptymultibulk);
        return;
    }
    if (end >= llen) end = llen-1;
    rangelen = (end-start)+1;       // Range length 

    /* Return the result in form of a multi-bulk reply */
    //  Send the number of reply lines to client
    addReplyMultiBulkLen(c, withscores ? (rangelen*2) : rangelen);

    //  If it is ziplist
    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {
    
        unsigned char *zl = zobj->ptr;
        unsigned char *eptr, *sptr;
        unsigned char *vstr;
        unsigned int vlen;
        long long vlong;

        //  according to reverse Determine the node address of the traversal head or tail element eptr
        if (reverse)
            eptr = ziplistIndex(zl,-2-(2*start));
        else
            eptr = ziplistIndex(zl,2*start);

        serverAssertWithInfo(c,zobj,eptr != NULL);
        //  Get the address of the score node 
        sptr = ziplistNext(zl,eptr);

        // Take out rangelen Elements 
        while (rangelen--) {
    
            serverAssertWithInfo(c,zobj,eptr != NULL && sptr != NULL);
            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));   // The element information is saved in parameters 
            //  The types of elements are different , Send different types of reply messages 
            if (vstr == NULL)
                addReplyBulkLongLong(c,vlong);
            else
                addReplyBulkCBuffer(c,vstr,vlen);
            //  Set up withscores, Send score 
            if (withscores)
                addReplyDouble(c,zzlGetScore(sptr));

            //  according to reverse, The node that points to the next element and score 
            if (reverse)
                zzlPrev(zl,&eptr,&sptr);
            else
                zzlNext(zl,&eptr,&sptr);
        }

    //  If it's a jump table 
    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
    
        zset *zs = zobj->ptr;
        zskiplist *zsl = zs->zsl;
        zskiplistNode *ln;
        robj *ele;

        /* Check if starting point is trivial, before doing log(N) lookup. */
        //  according to reverse decision , The node address of the traversal head or tail element 
        if (reverse) {
    
            ln = zsl->tail;
            if (start > 0)
                ln = zslGetElementByRank(zsl,llen-start);
        } else {
    
            ln = zsl->header->level[0].forward;
            if (start > 0)
                ln = zslGetElementByRank(zsl,start+1);
        }

        //  Take out rangelen Elements 
        while(rangelen--) {
    
            serverAssertWithInfo(c,zobj,ln != NULL);
            ele = ln->obj;
            addReplyBulk(c,ele);    // Send element 
            if (withscores)
                addReplyDouble(c,ln->score);    // Send score 
            //  Point to next node 
            ln = reverse ? ln->backward : ln->level[0].forward;
        }
    } else {
    
        serverPanic("Unknown sorted set encoding");
    }
}

4.2.2 ZRANGEBYSCORE

// ZRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
// ZRANGEBYSCORE  Command implementation 
void zrangebyscoreCommand(client *c) {
    
    genericZrangebyscoreCommand(c,0);
}

// ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
// ZREVRANGEBYSCORE  Command implementation 
void zrevrangebyscoreCommand(client *c) {
    
    genericZrangebyscoreCommand(c,1);
}

Underlying implementation

/* This command implements ZRANGEBYSCORE, ZREVRANGEBYSCORE. */
// ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
// ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
// ZRANGEBYSCORE ZREVRANGEBYSCORE The underlying implementation of the command 
void genericZrangebyscoreCommand(client *c, int reverse) {
    
    zrangespec range;
    robj *key = c->argv[1];
    robj *zobj;
    long offset = 0, limit = -1;
    int withscores = 0;
    unsigned long rangelen = 0;
    void *replylen = NULL;
    int minidx, maxidx;

    /* Parse the range arguments. */
    //  analysis min and max Subscript of the parameter of the range 
    if (reverse) {
    
        /* Range is given as [max,min] */
        maxidx = 2; minidx = 3;
    } else {
    
        /* Range is given as [min,max] */
        minidx = 2; maxidx = 3;
    }

    //  analysis min and max Range parameter , Save to zrangespec in , The default is to include [] critical value 
    if (zslParseRange(c->argv[minidx],c->argv[maxidx],&range) != C_OK) {
    
        addReplyError(c,"min or max is not a float");
        return;
    }

    /* Parse optional extra arguments. Note that ZCOUNT will exactly have * 4 arguments, so we'll never enter the following code path. */
    //  Parse other parameters 
    if (c->argc > 4) {
    
        int remaining = c->argc - 4;    // Number of other parameters 
        int pos = 4;

        while (remaining) {
    
            //  If there is WITHSCORES, Set logo 
            if (remaining >= 1 && !strcasecmp(c->argv[pos]->ptr,"withscores")) {
    
                pos++; remaining--;
                withscores = 1;
            //  If there is LIMIT, Take out offset and count value   Save in offset and limit in 
            } else if (remaining >= 3 && !strcasecmp(c->argv[pos]->ptr,"limit")) {
    
                if ((getLongFromObjectOrReply(c, c->argv[pos+1], &offset, NULL) != C_OK) ||
                    (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != C_OK)) return;
                pos += 3; remaining -= 3;
            } else {
    
                addReply(c,shared.syntaxerr);
                return;
            }
        }
    }

    /* Ok, lookup the key and get the range */
    //  Fetch an ordered collection object with a read operation 
    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptymultibulk)) == NULL ||
        checkType(c,zobj,OBJ_ZSET)) return;

    // ziplist
    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {
    
        unsigned char *zl = zobj->ptr;
        unsigned char *eptr, *sptr;
        unsigned char *vstr;
        unsigned int vlen;
        long long vlong;
        double score;

        /* If reversed, get the last node in range as starting point. */
        //  get range The starting element node address in the range 
        if (reverse) {
    
            eptr = zzlLastInRange(zl,&range);
        } else {
    
            eptr = zzlFirstInRange(zl,&range);
        }

        /* No "first" element in the specified interval. */
        //  There are no elements in range in , Send an empty reply 
        if (eptr == NULL) {
    
            addReply(c, shared.emptymultibulk);
            return;
        }

        /* Get score pointer for the first element. */
        serverAssertWithInfo(c,zobj,eptr != NULL);
        //  Score node address 
        sptr = ziplistNext(zl,eptr);

        /* We don't know in advance how many matching elements there are in the * list, so we push this object that will represent the multi-bulk * length in the output buffer, and will "fix" it later */
        //  Reply length 
        replylen = addDeferredMultiBulkLength(c);

        /* If there is an offset, just traverse the number of elements without * checking the score because that is done in the next loop. */
        //  skip offset Set length 
        while (eptr && offset--) {
    
            if (reverse) {
    
                zzlPrev(zl,&eptr,&sptr);
            } else {
    
                zzlNext(zl,&eptr,&sptr);
            }
        }

        //  Traverse all nodes that meet the scope 
        while (eptr && limit--) {
    
            score = zzlGetScore(sptr);  // Get points 

            /* Abort when the node is no longer in range. */
            //  Check whether the score meets the score within the range 
            if (reverse) {
    
                if (!zslValueGteMin(score,&range)) break;
            } else {
    
                if (!zslValueLteMax(score,&range)) break;
            }

            /* We know the element exists, so ziplistGet should always succeed */
            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));   // Save the value of the current element into the parameter 

            rangelen++;
            //  Different element types , Send different types of values to client
            if (vstr == NULL) {
    
                addReplyBulkLongLong(c,vlong);
            } else {
    
                addReplyBulkCBuffer(c,vstr,vlen);
            }

            if (withscores) {
    
                addReplyDouble(c,score);    // Send score 
            }

            /* Move to next node */
            //  Point to the next element and score node 
            if (reverse) {
    
                zzlPrev(zl,&eptr,&sptr);
            } else {
    
                zzlNext(zl,&eptr,&sptr);
            }
        }
    //  Skip list 
    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
    
        zset *zs = zobj->ptr;
        zskiplist *zsl = zs->zsl;
        zskiplistNode *ln;

        /* If reversed, get the last node in range as starting point. */
        //  get range The starting node address in the range 
        if (reverse) {
    
            ln = zslLastInRange(zsl,&range);
        } else {
    
            ln = zslFirstInRange(zsl,&range);
        }

        /* No "first" element in the specified interval. */
        //  No element exists within the specified scope 
        if (ln == NULL) {
    
            addReply(c, shared.emptymultibulk);
            return;
        }

        /* We don't know in advance how many matching elements there are in the * list, so we push this object that will represent the multi-bulk * length in the output buffer, and will "fix" it later */
        //  Reply length 
        replylen = addDeferredMultiBulkLength(c);

        /* If there is an offset, just traverse the number of elements without * checking the score because that is done in the next loop. */
        //  skip offset Nodes 
        while (ln && offset--) {
    
            if (reverse) {
    
                ln = ln->backward;
            } else {
    
                ln = ln->level[0].forward;
            }
        }

        //  Traverse all nodes that meet the scope 
        while (ln && limit--) {
    
            /* Abort when the node is no longer in range. */
            //  Check whether the score meets 
            if (reverse) {
    
                if (!zslValueGteMin(ln->score,&range)) break;
            } else {
    
                if (!zslValueLteMax(ln->score,&range)) break;
            }

            rangelen++;
            addReplyBulk(c,ln->obj);    // Send element 

            if (withscores) {
    
                addReplyDouble(c,ln->score);    // Send score 
            }

            /* Move to next node */
            //  Point to next node 
            if (reverse) {
    
                ln = ln->backward;
            } else {
    
                ln = ln->level[0].forward;
            }
        }
    } else {
    
        serverPanic("Unknown sorted set encoding");
    }

    if (withscores) {
       // Update reply length 
        rangelen *= 2;
    }

    setDeferredMultiBulkLength(c, replylen, rangelen);  // The length of the sending range 
}

4.2.3 ZRANGEBYLEX

// ZRANGEBYLEX key min max [LIMIT offset count]
// ZRANGEBYLEX  Command implementation 
void zrangebylexCommand(client *c) {
    
    genericZrangebylexCommand(c,0);
}

// ZREVRANGEBYLEX key min max [LIMIT offset count]
// ZREVRANGEBYLEX Command implementation 
void zrevrangebylexCommand(client *c) {
    
    genericZrangebylexCommand(c,1);
}

Underlying implementation

/* This command implements ZRANGEBYLEX, ZREVRANGEBYLEX. */
// ZRANGEBYLEX key min max [LIMIT offset count]
// ZRANGEBYLEX  Command implementation 
void genericZrangebylexCommand(client *c, int reverse) {
    
    zlexrangespec range;
    robj *key = c->argv[1];
    robj *zobj;
    long offset = 0, limit = -1;
    unsigned long rangelen = 0;
    void *replylen = NULL;
    int minidx, maxidx;

    /* Parse the range arguments. */
    //  analysis min and max Subscript of the parameter of the range 
    if (reverse) {
    
        /* Range is given as [max,min] */
        maxidx = 2; minidx = 3;
    } else {
    
        /* Range is given as [min,max] */
        minidx = 2; maxidx = 3;
    }

    //  analysis min and max Parameters of the range , Save to range
    if (zslParseLexRange(c->argv[minidx],c->argv[maxidx],&range) != C_OK) {
    
        addReplyError(c,"min or max not valid string range item");
        return;
    }

    /* Parse optional extra arguments. Note that ZCOUNT will exactly have * 4 arguments, so we'll never enter the following code path. */
    //  Parse other parameters 
    if (c->argc > 4) {
    
        int remaining = c->argc - 4;    // Number of other parameters 
        int pos = 4;

        while (remaining) {
    
            //  If there is LIMIT, Take out offset and count Save in offset and limit
            if (remaining >= 3 && !strcasecmp(c->argv[pos]->ptr,"limit")) {
    
                if ((getLongFromObjectOrReply(c, c->argv[pos+1], &offset, NULL) != C_OK) ||
                    (getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != C_OK)) return;
                pos += 3; remaining -= 3;
            } else {
    
                zslFreeLexRange(&range);
                addReply(c,shared.syntaxerr);
                return;
            }
        }
    }

    /* Ok, lookup the key and get the range */
    //  Fetch an ordered collection object with a read operation 
    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptymultibulk)) == NULL ||
        checkType(c,zobj,OBJ_ZSET))
    {
    
        zslFreeLexRange(&range);
        return;
    }

    // ziplist
    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {
    
        unsigned char *zl = zobj->ptr;
        unsigned char *eptr, *sptr;
        unsigned char *vstr;
        unsigned int vlen;
        long long vlong;

        /* If reversed, get the last node in range as starting point. */
        //  get range The starting element node address in the range 
        if (reverse) {
    
            eptr = zzlLastInLexRange(zl,&range);
        } else {
    
            eptr = zzlFirstInLexRange(zl,&range);
        }

        /* No "first" element in the specified interval. */
        //  No element is in scope 
        if (eptr == NULL) {
    
            addReply(c, shared.emptymultibulk);
            zslFreeLexRange(&range);
            return;
        }

        /* Get score pointer for the first element. */
        serverAssertWithInfo(c,zobj,eptr != NULL);
        //  The address of the score node 
        sptr = ziplistNext(zl,eptr);

        /* We don't know in advance how many matching elements there are in the * list, so we push this object that will represent the multi-bulk * length in the output buffer, and will "fix" it later */
        //  Reply length 
        replylen = addDeferredMultiBulkLength(c);

        /* If there is an offset, just traverse the number of elements without * checking the score because that is done in the next loop. */
        //  skip offset Set length 
        while (eptr && offset--) {
    
            if (reverse) {
    
                zzlPrev(zl,&eptr,&sptr);
            } else {
    
                zzlNext(zl,&eptr,&sptr);
            }
        }

        //  Traverse all nodes that meet the scope 
        while (eptr && limit--) {
    
            /* Abort when the node is no longer in range. */
            //  Check whether the score meets the score within the range 
            if (reverse) {
    
                if (!zzlLexValueGteMin(eptr,&range)) break;
            } else {
    
                if (!zzlLexValueLteMax(eptr,&range)) break;
            }

            /* We know the element exists, so ziplistGet should always * succeed. */
            serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));   // Save the value of the current element into the parameter 

            rangelen++;
            //  Different element value types , Send different types of values client
            if (vstr == NULL) {
    
                addReplyBulkLongLong(c,vlong);
            } else {
    
                addReplyBulkCBuffer(c,vstr,vlen);
            }

            /* Move to next node */
            //  Point to the next element and score node 
            if (reverse) {
    
                zzlPrev(zl,&eptr,&sptr);
            } else {
    
                zzlNext(zl,&eptr,&sptr);
            }
        }
    // skiplist
    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
    
        zset *zs = zobj->ptr;
        zskiplist *zsl = zs->zsl;
        zskiplistNode *ln;

        /* If reversed, get the last node in range as starting point. */
        //  get range The starting node address in the range 
        if (reverse) {
    
            ln = zslLastInLexRange(zsl,&range);
        } else {
    
            ln = zslFirstInLexRange(zsl,&range);
        }

        /* No "first" element in the specified interval. */
        //  No nodes meet the scope 
        if (ln == NULL) {
    
            addReply(c, shared.emptymultibulk);
            zslFreeLexRange(&range);
            return;
        }

        /* We don't know in advance how many matching elements there are in the * list, so we push this object that will represent the multi-bulk * length in the output buffer, and will "fix" it later */
        //  Reply length 
        replylen = addDeferredMultiBulkLength(c);

        /* If there is an offset, just traverse the number of elements without * checking the score because that is done in the next loop. */
        //  skip offset Nodes 
        while (ln && offset--) {
    
            if (reverse) {
    
                ln = ln->backward;
            } else {
    
                ln = ln->level[0].forward;
            }
        }

        //  Traverse all nodes that meet the scope 
        while (ln && limit--) {
    
            /* Abort when the node is no longer in range. */
            if (reverse) {
      // Check whether the score meets 
                if (!zslLexValueGteMin(ln->obj,&range)) break;
            } else {
    
                if (!zslLexValueLteMax(ln->obj,&range)) break;
            }

            rangelen++;
            addReplyBulk(c,ln->obj);    // Send element 

            /* Move to next node */
            //  Point to next node 
            if (reverse) {
    
                ln = ln->backward;
            } else {
    
                ln = ln->level[0].forward;
            }
        }
    } else {
    
        serverPanic("Unknown sorted set encoding");
    }

    zslFreeLexRange(&range);    // Release dictionary order range 
    setDeferredMultiBulkLength(c, replylen, rangelen);  // Send reply length value 
}


Reference material :

《Redis Design and implementation 》

Redis Source code analysis and comments

原网站

版权声明
本文为[JunesFour]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/204/202207230451475310.html