[take you to redis from the beginning to the beginning] to understand the jump table, this is probably not enough

Time：2022-5-14

preface

Oh shredded pork
Oh, Jack
you jump! i jump!
Yeah is immersed in the joy of jump
Let’s talk to you today`redis`Jump table

The jump table that everyone needs to know is not only the concept but also the code. It is strongly recommended to read redis5 design and source code analysis edited by Chen Lei

Ordered set

Let’s take a look at what we usually use about`redis`of`Ordered set`
The commonly used commands are as follows

``````127.0.0.1:6379> zadd jump 1 jack
(integer) 1
(integer) 1
127.0.0.1:6379> zrange jump 0 1
1) "jack"
2) "rose"``````

`redis`Complexity of ordered sets
`ZADD`O (m * log (n)), n is the cardinality of the ordered set, and M is the number of new members successfully added.
`ZRANGE`O (log (n) + m), where n is the cardinality of the ordered set and M is the cardinality of the result set
`ZRANK` O(log(N))
`ZSCORE` O(1)

We might as well think about what kind of data structure can meet the above requirements, and take out our data structure knowledge reserve at this time.`Linear table``Linked list``Stack and queue``strand``Tree it family series`Think about which one fits the above

It is also a common question why to use a jump table as a data structure

1. Linear table lookup matches addition and deletion. O (n) is not allowed
2. The addition and deletion of linked list match the search is O (n)
3. It seems that the complexity of the tree (no matter what binary, red and black, B and its family) can be satisfied, but let’s have a look`zscore`It’s o (1). It doesn’t seem to be internal flavor

And how painful the tree is. You can often hear the following dialogue
Can you tear the * * tree by hand
I can climb * * trees. Do you think so?

Zscore returns the score value of the member in the ordered set key. If it is O (1), it tastes like a linked list

So I guess the jump list is the illegitimate child of the linked list, definitely not the illegitimate child of the tree.

Jump table

In fact, a jump list is a multi-layer ordered two-way linked list

There is a three storey building. Everyone can use shadow separation. You can put your own shadow on each floor (it depends on everyone’s mood. Once you decide, you can’t regret it), and each room has stairs leading to the next floor. There are three people here: dog egg a, dog egg B and dog egg C. one exam is over
Dog egg a got 60 points
Dog egg B got 80 points
Dog egg C got 100 points
Each dog egg arranges its own order according to the score, and those who do well stand behind.
Dog egg his father lives on the top floor (the second floor) and wants to ask who got 100 points
It looks like this

1. Dog egg his father asked dog egg a: what’s your score. Dog egg a said: 60, there are no dog eggs behind this floor.
2. Dog egg his father is walking down the floor in dog egg a’s room.
4. I saw dog egg B: dog egg B said 80 points, and there was no dog egg in the back.
5. Dog egg his father walked down in dog egg B’s room and found dog egg C, which was the one who got 100.
The route is probably like this

Some people will say that this is not as good as an orderly linked list. What’s the stratification

After all, Hong Guangguang painted the picture blindly to save trouble. Let’s refine the picture and taste the benefits of this structure

After reading the source code, you will find that the height of each dog egg is really the mood of watching dog eggs.

Analysis source code

Someone said: I also understand the concept of jump table. I have the ability to directly roll out the source code.
Come on, let’s go

structural morphology

``````/**
*Structure of jump table node
*/
typedef struct zskiplistNode {
//Use the type of data key (member) of dynamic string if the header node is null
sds ele;
//The corresponding sorting score if the header node is null
double score;
//The ring head pointing to the previous node is null
struct zskiplistNode *backward;
//The length of the flexible array is from random level 64 to random level
struct zskiplistLevel {
//The ring head pointing to the previous node is null
struct zskiplistNode *forward;
//Indicates the number of nodes under this layer
unsigned long span;
} level[];
} zskiplistNode;

/**
*Jump table structure
*/
typedef struct zskiplist {
//Point to the head and tail of the jump table
//Skip table length remove header node
unsigned long length;
//Level of jump table
int level;
} zskiplist;``````

According to the structure of the above dog egg diagram and jump table, see if you can have an implementation idea in mind. If so, don’t look down. I’m afraid you’ll be biased by me.

Basic knowledge points of jump table

1. The maximum floor height is 64 floors. The specific floor height depends on the mood (randomly generated, the greater the probability, the lower the probability)
2. Each layer is an ordered two-way linked list (with forward pointer and backward pointer)
3. The header node is a dormitory similar to a University (know the number of rooms on each floor and the pointer of the next room on the current floor)
4. The last node of each layer points to null (that is, judge whether it is the last node of this layer with the next node being null)
5. The number of nodes on layer 0, that is, the last layer, is the actual length of the jump table (think of the above example, the foundation is laid on the first layer, of course, you can know the whole specific length)

Source code analysis

zslRandomLevel

``````#define ZSKIPLIST_MAXLEVEL 64 /* Should be enough for 2^64 elements */
#define ZSKIPLIST_P 0.25      /* Skiplist P = 1/4 */``````
``````/**
*Floor height of random production jump table node
* @return
*/
int zslRandomLevel(void) {
//Default floor height: 1 floor
int level = 1;
//& 0xFFFF = 65535, only take the lower 16 bits, which is equivalent to only get 1 ~ 65535
// ZSKIPLIST_P -> 0.25
while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
level += 1;
//The final probability is 1 / (1 - zskiplist_p)
return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}``````

You see, the height of each dog egg really depends on the mood.

zslCreate

``````/**
*Initialize jump table
* @return
*/
zskiplist *zslCreate(void) {
int j;
zskiplist *zsl;
//Initialize jump table memory
zsl = zmalloc(sizeof(*zsl));
//Default layer
zsl->level = 1;
//The default length is 0
zsl->length = 0;
//The default jump table is 64 layers
for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
}
//Backward pointer
//Tail pointer
zsl->tail = NULL;
return zsl;
}``````

zslInsert

``````/**Insert jump table node
*
*@ param ZSL manage jump table
*@ param score
*@ param ele string
* @return
*/
zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
unsigned int rank[ZSKIPLIST_MAXLEVEL];
int i, level;

serverAssert(!isnan(score));
for (i = zsl->level-1; i >= 0; i--) {
rank[i] = i == (zsl->level-1) ? 0 : rank[i+1];
//There is a lower node and the next node is less than the current score (if the node scores are the same, compare the key Dictionary)
while (x->level[i].forward &&
(x->level[i].forward->score < score ||
(x->level[i].forward->score == score &&
sdscmp(x->level[i].forward->ele,ele) < 0)))
{
//Save steps for each layer
rank[i] += x->level[i].span;
x = x->level[i].forward;
}
//Record each front node
update[i] = x;
}

//Update [i] records the front node of each layer
//Rank [i] record the step size to be updated for each layer

//Initialize the floor height of the node to be inserted
level = zslRandomLevel();
//If the level height of the inserted node is greater than the level height of the jump table
if (level > zsl->level) {
//The extra floor height needs to be
for (i = zsl->level; i < level; i++) {
rank[i] = 0;
update[i]->level[i].span = zsl->length;
}
zsl->level = level;
}

//Create a node that needs to be inserted into the jump table
x = zslCreateNode(level,score,ele);
for (i = 0; i < level; i++) {
//Each layer is an ordered linked list, which is inserted by reference
x->level[i].forward = update[i]->level[i].forward;
update[i]->level[i].forward = x;
x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
//Update step
update[i]->level[i].span = (rank[0] - rank[i]) + 1;
}
//Because inserting a node requires + 1 span for each layer
for (i = level; i < zsl->level; i++) {
update[i]->level[i].span++;
}
//The backward pointer of each node points to the previous node of the first layer. If the inserted node is not the head node
x->backward = (update[0] == zsl->header) ? NULL : update[0];
//If there is a node after the inserted node
if (x->level[0].forward)
//Then the backward pointer of the following node points to itself
x->level[0].forward->backward = x;
else
//If there is no node, the inserted node is the last node, and the tail pointer points to itself
zsl->tail = x;
//Increase the length of the jump table because a node is inserted
zsl->length++;
return x;
}``````

It doesn’t matter if you don’t understand the specific code implementation now, because this article doesn’t intend to let you understand the specific implementation. You can download the specific code in redis version 5.0 by yourself_ zset. C file or you can buy redis5 design and source code analysis edited by Chen Lei (the latter is strongly recommended).

last

If you become an interviewer in the future, ask others what is the underlying implementation of an orderly collection?
If that person says it’s a jump watch.
Please go back and use the bottom implementation of the ordered collection`Jump table`and`Compressed list`Configure according to parameters`zset-max-ziplust-entris`and`zset-max-ziplist-value`decision

`````` /* Lookup the key and create the sorted set if does not exist. */
zobj = lookupKeyWrite(c->db,key);
if (zobj == NULL) {
if (xx) goto reply_to_client; /* No key + XX option: nothing to do. */
if (server.zset_max_ziplist_entries == 0 ||
server.zset_max_ziplist_value < sdslen(c->argv[scoreidx+1]->ptr))
{
zobj = createZsetObject();
} else {
zobj = createZsetZiplistObject();
}