“This is my 40th day of participating in the First Challenge 2022. For more details: First Challenge 2022.”

The data structure

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;
Copy the code

There are two implementations

1. The first ziplist node holds the element’s members, while the second node holds the element’s score. The collection elements in the compressed list are sorted by branch from smallest to largest, with the elements with the lowest score placed near the table head and the elements with the highest score placed near the table end.

Skiplist actually uses the Zset structure to encapsulate dictionaries and skip lists. The ZSL skip list branch in the Zset structure holds all elements from small to large. With this skip list, programs can perform range operations on ordered sets, such as Zrank, Zrange and other commands are based on the skip list to achieve.

Ziplist inserts data

/* Insert (element,score) pair in listpack. This function assumes the element is * not yet present in the list. */
unsigned char *zzlInsert(unsigned char *zl, sds ele, double score) {
    unsigned char *eptr = lpSeek(zl,0), *sptr;
    double s;
    
    // The sorting process
    while(eptr ! =NULL) { sptr = lpNext(zl,eptr); serverAssert(sptr ! =NULL);
        s = zzlGetScore(sptr);

        if (s > score) {
            /* First element with score larger than score for element to be * inserted. This means we should take its spot in the list to * maintain ordering. */
            // element, score
            zl = zzlInsertAt(zl,eptr,ele,score);
            break;
        } else if (s == score) {
            /* Ensure lexicographical ordering for elements. */
            if (zzlCompareElements(eptr,(unsigned char*)ele,sdslen(ele)) > 0) {
                zl = zzlInsertAt(zl,eptr,ele,score);
                break; }}/* Move to next element. */
        eptr = lpNext(zl,sptr);
    }

    /* Push on tail of list when it was not yet inserted. */
    if (eptr == NULL)
        zl = zzlInsertAt(zl,NULL,ele,score);
    return zl;
}
Copy the code

Ziplist compressed lists are switched to skip lists

/* Convert the sorted set object into a listpack if it is not already a listpack
 * and if the number of elements and the maximum element size and total elements size
 * are within the expected ranges. */
void zsetConvertToListpackIfNeeded(robj *zobj, size_t maxelelen, size_t totelelen) {
    if (zobj->encoding == OBJ_ENCODING_LISTPACK) return;
    zset *zset = zobj->ptr;

    if (zset->zsl->length <= server.zset_max_listpack_entries &&
        maxelelen <= server.zset_max_listpack_value &&
        lpSafeToAdd(NULL, totelelen))
    {
        zsetConvert(zobj,OBJ_ENCODING_LISTPACK);
    }
}
Copy the code

Pay attention to the point

On the basis of skiplist, dict needs to be created because when the score of an element needs to be obtained, the complexity of skiplist is O(logN), while the complexity of dict is O(1), see (zsetAdd). In particular, the main thing is that when the bottom layer is Ziplist, the operation time is O(n).

int zsetScore(robj *zobj, sds member, double *score) { if (! zobj || ! member) return C_ERR; if (zobj->encoding == OBJ_ENCODING_LISTPACK) { if (zzlFind(zobj->ptr, member, score) == NULL) return C_ERR; } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { zset *zs = zobj->ptr; dictEntry *de = dictFind(zs->dict, member); if (de == NULL) return C_ERR; *score = *(double*)dictGetVal(de); } else { serverPanic("Unknown sorted set encoding"); } return C_OK; } unsigned char *zzlFind(unsigned char *lp, sds ele, double *score) { unsigned char *eptr, *sptr; if ((eptr = lpFirst(lp)) == NULL) return NULL; eptr = lpFind(lp, eptr, (unsigned char*)ele, sdslen(ele), 1); if (eptr) { sptr = lpNext(lp,eptr); serverAssert(sptr ! = NULL); /* Matching element, pull out score. */ if (score ! = NULL) *score = zzlGetScore(sptr); return eptr; } return NULL; }Copy the code
  • Zkiplist and dict share elements and points (pointer copying).
  • The conversion from Zkiplist to skiplist is irreversible
  • The two parameters zset-max-ziplist-entries and zset-max-ziplist-value can be changed in the configuration file
  • Zset also does not allow duplication

Usage scenarios

  • Priority queue

  • Leaderboard system: video websites mainly need to make leaderboards for videos uploaded by users. The dimensions of the leaderboard are in many aspects: ranking by time, by play amount and by likes obtained

    • Example Add a user’s likes
    zadd user:ranking:2022_02_20 3 mike
    Copy the code
    • Increase the thumb up
    zincrby user:ranking:2020_02_20 1 mike
    Copy the code
    • Cancel the thumb up
    zrem user:ranking:2020_02_20 mike
    Copy the code
    • Temporarily get the 10 users with the most likes
    zrevrang user:rangking:2020_02_20 0 9
    Copy the code
    • Displays user information as well as user scores and rankings
    hgetall user:info:tom
    zscore user:rangking:2020_02_20 mike
    zrank user:rangking:2020_02_20 mike
    Copy the code

Common operation

Redis object summary

The data type Usage scenarios note
String (string) The cache. The counter; A distributed lock Simple, such as set strnum studentinfo. Counter such as current limiting
List Lpush + LPOP = Stack lPUSH + RPOP = Queue Lpush + Ltrim = Capped Collection Lpush + BRPOP = Message Queue: Message Queue Such as blocking queues, focus on lists
Hash Object properties (especially variable length) Example cache studentinfo hmset setnum setnum 1 Stuname dinghaijun age 33
Set Suitable for social/recommendation scenarios Likes, fans, common hobbies/likes, push
Ordered set (Zset) List; Priority queue; Caching related metadata (such as sorting challenges)