Link List of Redis underlying data structure

Keywords: Redis Programming Java

Link List of Redis underlying data structure

I. Realization of Link List in Redis

We all know how to insert and delete lists. If an element is inserted in the middle of an array, the memory address of all elements after that element moves backwards. Deleting is the same as inserting and deleting the last element of the data. Link list does not need to change the memory address of nodes. The advantage of link list is to add and delete. The search time complexity is O(n). Link list has better expansibility than array. As an important data structure, linked list is widely used to realize various functions of redis. Because we have learned linked list in data structure/algorithm many times, there is no verbosity here, and we can code it directly.

The definition of linked list data structure in Redis is listed Node structure in Adlist.h file. The concrete results are as follows:

    /* Node, List, and Iterator are the only data structures used currently. */
    /* listNode node */
    typedef struct listNode {
        //The preceding node of a node
        struct listNode *prev;
        //Next node of node
        struct listNode *next;
        //Values of nodes
        void *value;
    } listNode;

This is the basic definition of the linked list node, but in order to realize the operation of the linked list and facilitate user calls, Redis has made a layer of encapsulation, using list to hold the linked list, the specific structure definition is as follows:

/* listNode list */
    typedef struct list {
        //Chain Head Node
        listNode *head;
        //End of list
        listNode *tail;

        /* The following three pointer functions are common methods for all nodes, which are equivalent to the methods of our classes in object-oriented programming.*/
        // Replicate the values saved by the linked list node
        void *(*dup)(void *ptr);
        // Release the value saved by the linked list node
        void (*free)(void *ptr);
        // Is the value matching two nodes equal
        int (*match)(void *ptr, void *key);
        // Link List Length
        unsigned long len;
    } list;

Another data structure is iterator, familiar with C++ STL or Java collection, should be more familiar with, iterator is to encapsulate the collection (here is linked list) traversal rules, so that users do not have to adhere to traversal details, the following is the structure definition of linked list in Redis.

    /* list Iterator, can only be one-way */
    typedef struct listIter {
        //Next node at the current iteration position
        listNode *next;
        //Direction of iterator
        int direction;
    } listIter;

After enumerating the data structure of the linked list, we will focus on several functions with code to explain the specific implementation process of the linked list. First, we will look at the lookup function listSearchKey.

/* Search the list for a node matching a given key.
     * The match is performed using the 'match' method
     * set with listSetMatchMethod(). If no 'match' method
     * is set, the 'value' pointer of every node is directly
     * compared with the 'key' pointer.
     *
     * On success the first matching node pointer is returned
     * (search starts from head). If no matching node exists
     * NULL is returned. */
    /* Find if there is a key-valued node in the linked list, then return to the changed node, otherwise return to NULL. */
    listNode *listSearchKey(list *list, void *key)
    {
        // Here we use an iterator, and then we can see how to iterate through it.
        listIter *iter;
        listNode *node;

        //Get the iterator, traversing backwards from head
        iter = listGetIterator(list, AL_START_HEAD);
        //foreach
        while((node = listNext(iter)) != NULL) {
           //If the list defines a match method, the match method is called to compare the values of the nodes.
            if (list->match) {
                if (list->match(node->value, key)) {
                    //If the function returns true, it means finding the node, releasing the iterator space, and returning the found node.
                    listReleaseIterator(iter);
                    return node;
                }
            } else {
                //If no match method of list is defined, function pointers are directly compared
                if (key == node->value) {
                    //If equal, it means finding the node and releasing the iterator
                    listReleaseIterator(iter);
                    return node;
                }
            }
        }
        listReleaseIterator(iter);
        return NULL;
    }

Next, let's look at the implementation of a function. Head interpolation joins the node listAddNodeHead.

/* Add a new node to the list, to head, contaning the specified 'value'
     * pointer as value.
     *
     * On error, NULL is returned and no operation is performed (i.e. the
     * list remains unaltered).
     * On success the 'list' pointer you pass to the function is returned. */
     /* Head interpolation joining nodes */
    list *listAddNodeHead(list *list, void *value)
    {
        listNode *node;
        //Define a new listNode and assign function pointers
        if ((node = zmalloc(sizeof(*node))) == NULL)
            return NULL;
        node->value = value;
        if (list->len == 0) {
            //When there is no node at this time, the head and end are the same node, and the front and back pointers are NULL.
            list->head = list->tail = node;
            node->prev = node->next = NULL;
        } else {
            //Set the location relationship between this node next and the front node
            node->prev = NULL;
            node->next = list->head;
            list->head->prev = node;
            list->head = node;
        }
        //Node count increments and returns
        list->len++;
        return list;
    }

Link list of other functions, here not one by one analysis, interested friends, the Redis source code, download, see it.

Here's a summary of the benefits of Redis's dual-ended list implementation:

  • Double-end linked list can realize the practical complexity of O(1) before and after accessing nodes
  • Ring-free, the precursor node of the table head and the successor node of the table tail point to NULL
  • With header and tail nodes, the time complexity of program acquisition of header and tail is O(1)
  • Using the chain length counter, the chain length can be obtained within the time complexity of O(1)
  • Linked list nodes use void* pointers to save node values, and can set type-specific functions for node values through dup, free, match attributes of list structure, so linked list can be used to store different types of values.

Posted by timelf123 on Sat, 13 Jul 2019 14:53:59 -0700