Hardcore! 15 illustrations of why Redis is so fast

As a server engineer, you must have dealt with Redis at work. Why Redis is fast, you must know this too, at least you have prepared for the interview. Many people know that Redis is fast only because it is implemented based on memory, but it is ambiguous for other reasons.

So let’s take a look with Xiao Lai today:

Based on memory

This point was mentioned at the beginning, so let's briefly talk about it here.

Redis is a memory-based database, so it is inevitable to compare it with a disk database. For disk databases, data needs to be read into memory, and this process will be limited by disk I/O.

For an in-memory database, the data itself exists in memory, so there is no overhead in this area.

Efficient data structure

There are multiple data types in Redis, and the bottom layer of each data type is supported by one or more data structures. It is precisely because of these data structures that Redis's storage and reading speed is not hindered. What's so special about these data structures, everyone, then look down:

1. Simple dynamic string

This term may be unfamiliar to you, but you  will know it if you change it to  SDS . This is used to process strings. Anyone who knows the C language knows that it has a method for processing strings. Redis is implemented in C language, so why repeat the wheel? We look at the following points:

(1) String length processing

 This picture is how the string is stored in the C language. To get the length of "Redis", you need to traverse from the beginning until it encounters'\0'.

How to operate in Redis? Use a len field to record the length of the current string. To get the length, you only need to get the len field. You see, the gap speaks for itself. The time complexity of the former traversal is O(n), which can be obtained by O(1) in Redis, and the speed is significantly improved.

(2) Memory reallocation

When it comes to modifying strings in C language, memory is reallocated. The more frequent the modification, the more frequent the memory allocation. And memory allocation will consume performance, so performance degradation is inevitable.

In Redis, frequent string modification operations are involved, and this memory allocation method is obviously not suitable. So SDS implemented two optimization strategies:

  • Space pre-allocation

When modifying and expanding the SDS, in addition to allocating the necessary space, the unused space will also be allocated.

The specific allocation rules are as follows: After the SDS is modified, if the length of len is less than 1M, then an additional unused space of the same length as len will be allocated. If the modified length is greater than 1M, then 1M of use space will be allocated.

  • Inert space release

Of course, if there is space allocation, there will be space release.

When the SDS is shortened, the excess memory space is not reclaimed, but the free field is used to record the extra space. If there are subsequent changes, the space recorded in free is used directly, reducing the memory allocation.

(3) Binary security

You already know that Redis can store various data types, and binary data is certainly no exception. However, binary data is not a regular string format, and may contain some special characters, such as'\0'. As we mentioned earlier, the string in C will end when it encounters'\0', and the data after'\0' cannot be read. But in SDS, the end of the string is judged based on the length of len. See, the problem of binary security is solved.

2. Double-ended linked list

List List is more used as a queue or stack. The characteristics of queues and stacks are first-in first-out and first-in-last-out. The double-ended linked list supports these characteristics well.

(1) Front and rear nodes

 

Each node in the linked list has two pointers, prev points to the previous node, and next points to the next node. In this way, the front and back nodes can be obtained within O(1) time complexity.

(2) Head and tail nodes

You may have noticed that there are two parameters head and tail in the head node, which point to the head node and the tail node respectively. Such a design can reduce the processing time complexity of double-ended nodes to O(1), which is perfect for queues and stacks. At the same time, the list can be iterated from both ends.

(3) The length of the linked list

There is also a parameter len in the head node, which is similar to the SDS mentioned above, here it is used to record the length of the linked list. Therefore, when obtaining the length of the linked list, you do not need to traverse the entire linked list, just get the value of len directly, and the time complexity is O(1).

You see, these features reduce the time cost of using List.

3. Compress the list

We are already familiar with double-ended linked lists. I don't know if you have noticed a problem: If you store a small data in a linked list node, such as a byte. Then the corresponding additional data such as the head node, front and rear pointers must be saved.

This wastes space, and at the same time, it is easy to cause memory fragmentation due to repeated applications and releases. In this way, the efficiency of memory usage is too low.

So, the compressed list comes on stage!

It is specially coded and designed specifically to improve memory usage efficiency. All operations are carried out through the pointer and the decoded offset.

And the memory of the compressed list is allocated continuously, and the traversal speed is very fast.

4. Dictionary

Redis is a KV database, and all key values ​​are stored in dictionaries. You should not be unfamiliar with the dictionary you use in your daily study. If you want to find a certain word, you can locate it directly through a certain word. The speed is very fast. The dictionaries mentioned here are the same in principle, and the corresponding value can be directly obtained through a certain key. A dictionary is also called a hash table. There is nothing to say about it. The characteristics of the hash table are well known to everyone, and the associated value can be retrieved and inserted in O(1) time complexity.

5. Jump table

As a unique data structure in Redis-the skip table, it adds a multi-level index on the basis of the linked list to improve the search efficiency.

This is a simple schematic diagram of a jumping list, each layer has an ordered linked list, and the lowest linked list contains all the elements. In this way, the hop table can support finding the corresponding node in the time complexity of O(logN). The following is the actual storage structure of the jump table. Like other data structures, the corresponding information is recorded in the head node, which reduces some unnecessary system overhead.

Reasonable data encoding

For each data type, the underlying support may be a variety of data structures, when to use which data structure, which involves the problem of encoding conversion.

Then let's take a look at how different data types are encoded and transformed:

String : if storing numbers, use int type encoding, if it is not a number, use raw encoding;

List : The length of the string and the number of elements are less than a certain range and use ziplist encoding. If any condition is not met, it will be converted to linkedlist encoding;

Hash : The length of the key and value string in the key-value pair saved by the hash object is less than a certain value and key-value pair;

Set : Save the elements as integers and the number of elements is less than a certain range and use intset coding, if any condition is not met, then use hashtable coding;

Zset : If the number of elements saved in the zset object is less than and the member length is less than a certain value, use ziplist encoding. If any condition is not met, use skiplist encoding.

Appropriate threading model

Another reason Redis is fast is because of the use of a suitable threading model:

1. I/O multiplexing model

  • I / O  : Network I / O

  • Multipath : multiple TCP connections

  • Reuse : share a thread or process

In the production environment, usually multiple clients connect to Redis, and then each send commands to the Redis server, and finally the server processes these requests and returns results.

In response to a large number of requests, Redis uses an I/O multiplexer to monitor multiple sockets at the same time, push these events to a queue, and then execute them one by one. Finally, the result is returned to the client.

2. Avoid context switching

You must have heard that Redis is single-threaded. So why is single-threaded Redis so fast?

Because multi-threading requires CPU context switching during execution, this operation is time-consuming. Redis is implemented based on memory. For memory, the efficiency without context switching is the highest. Multiple reads and writes are on one CPU, which is the best solution for memory.

3. Single-threaded model

By the way, why Redis is single-threaded.

Redis uses the Reactor single-threaded model, you may not be familiar with it. It’s okay, you just need to get an overview.

In this picture, after receiving the user's request, all are pushed to a queue, and then handed over to the file event dispatcher, which is a single-threaded working method. Redis is based on it again, so Redis is single-threaded.

to sum up

Based on memory

  • The data is stored in the memory, which reduces some unnecessary I/O operations, and the operation speed is very fast.

Efficient data structure

  • Multiple data structures at the bottom support different data types, and support Redis to store different data;

  • The design of different data structures minimizes the time complexity of data storage.

Reasonable data encoding

  • Adapt to different encoding formats according to the length of the string and the number of elements.

Appropriate threading model

  • The I/O multiplexing model monitors client connections at the same time;

  • Single thread does not need to perform context switching during execution, which reduces time-consuming.

 

Guess you like

Origin blog.csdn.net/Crystalqy/article/details/109289307