A review of the classic interview questions of Java Gang: Why is Redis so fast?

Source:
https://juejin.cn/post/6978280894704386079

foreword

Hello everyone, we all know that Redis is very fast, and its QPS can reach 100,000 (requests per second). Why is Redis so fast? This article will learn with you.

based on memory

We all know that memory reads and writes are much faster than disk reads and writes. Redis is a database implemented based on memory storage, which saves disk I/O consumption compared to databases where data is stored on disk. Disk databases such as MySQL need to build indexes to speed up query efficiency, while Redis data is stored in memory and operates directly in memory, so it is very fast.

efficient data structures

We know that MySQL index chooses the data structure of B+ tree in order to improve efficiency. In fact, a reasonable data structure can make your application/program faster. Let's first look at the data structure & internal coding diagram of Redis:

SDS Simple Dynamic String

struct sdshdr { //SDS简单动态字符串
    int len;    //记录buf中已使用的空间
    int free;   // buf中空闲空间长度
    char buf[]; //存储的实际内容
}

String length handling

In C language, to get the length of the string of the little boy who picked up the snails, you need to traverse from the beginning, and the complexity is O(n); in Redis, there is already a len field to record the length of the current string, you can get it directly That is, the time complexity is O(1).

Reduce the number of memory reallocations

In the C language, modifying a string requires reallocating memory. The more frequent the modification, the more frequently the memory is allocated, and allocating memory will consume performance . In Redis, SDS provides two optimization strategies: space pre-allocation and lazy space release.

space preallocation

When SDS simple dynamic string modification and space expansion, in addition to allocating the necessary memory space, additional unused space will be allocated. The distribution rules are sourdough:

After SDS is modified, if the length of len is less than 1M, an additional unused space of the same length as len will be allocated. For example, len=100, after reallocation, the actual length of buf will become 100 (used space) + 100 (extra space) + 1 (null character) = 201.

After SDS is modified, if the length of len is greater than 1M, the program will allocate 1M of unused space.

Inert space release

When the SDS is shortened, instead of reclaiming the excess memory space, use free to record the excess space. Subsequent modification operations will directly use the space in free to reduce memory allocation.

hash

As a KV in-memory database, Redis uses a global hash to store all key-value pairs. This hash table consists of multiple hash buckets. The entry element in the hash bucket stores the *key and *value pointers, where *key points to the actual key and *value points to the actual value.

Hash table lookup rate is very fast, somewhat similar to HashMap in Java , it allows us to quickly find key-value pairs in O(1)  time complexity. First calculate the hash value by key, find the corresponding hash bucket location, then locate the entry, and find the corresponding data in the entry.

Some friends may have doubts: when you write a large amount of data into the hash table, you will encounter the problem of hash collision , and the efficiency will drop.

Hash collision:  The same hash value is calculated through different keys, resulting in the same hash bucket.

Redis uses chained hashing to resolve hash collisions . Chained hashing means that in the same hash bucket, multiple elements are stored in a linked list, and they are connected by pointers in turn.

Some friends may still have questions: the elements on the hash collision chain can only be searched one by one through the pointer and then operated. When a lot of data is inserted into the hash table, there will be more conflicts, the longer the conflict linked list will be, and the query efficiency will be reduced.

In order to maintain efficiency, Redis will perform a rehash operation on the hash table , that is, adding hash buckets and reducing conflicts. In order to make rehash more efficient, Redis also uses two global hash tables by default, one for current use, called the main hash table, and one for expansion, called the standby hash table.

jump table

The jump table is a data structure unique to Redis. It is actually a multi-level index added to the linked list to improve search efficiency. A simple schematic of the skip table is as follows:

  • Each level has an ordered linked list, and the bottommost linked list contains all the elements.
  • The skip table supports average O(logN), worst O(N) node lookups, and can batch process nodes through sequential operations.

ziplist ziplist

Compressed list ziplist is one of the low-level implementations of list keys and dictionary keys. It is a list composed of a series of specially coded memory blocks. A ziplist can contain multiple entries, and each entry can hold a character array or integer of limited length, as follows:

  • zlbytes : record the number of bytes of memory occupied by the entire compressed list
  • zltail: the offset from the tail node to the start node
  • zllen : record the number of nodes contained in the entire compressed list
  • entryX: each node contained in the compressed list
  • zlend : special value 0xFF (255 decimal), used to mark the end of the compressed list

Since memory is allocated contiguously , traversal is fast. .

Reasonable data encoding

Redis supports a variety of basic data types, each basic type corresponds to a different data structure, and each data structure corresponds to a different encoding. In order to improve performance, Redis designers concluded that the data structure is the most suitable encoding match.

Redis uses objects (redisObject) to represent key values ​​in the database. When we create a key value pair in Redis, at least two objects are created, one object is the key object used as the key value pair, and the other is the key value The value object of the pair.

typedef struct redisObject{
    //类型
   unsigned type:4;
   //编码
   unsigned encoding:4;
   //指向底层数据结构的指针
   void *ptr;
    //...
 }robj;

In redisObject, type  corresponds to the object type, including String objects, List objects, Hash objects, Set objects, and zset objects. encoding  corresponds to encoding.

  • String: If a number is stored, it is encoded in int type; if a non-digital string less than or equal to 39 bytes is stored, it is embstr; if it is greater than 39 bytes, it is raw encoding.
  • List: If the number of elements in the list is less than 512, the value of each element of the list is less than 64 bytes (default), use ziplist encoding, otherwise use linkedlist encoding
  • Hash: The number of hash type elements is less than 512. If all values ​​are less than 64 bytes, use ziplist encoding, otherwise use hashtable encoding.
  • Set: If the elements in the set are all integers and the number of elements is less than 512, use intset encoding, otherwise use hashtable encoding.
  • Zset: When the number of elements in the ordered set is less than 128 and the value of each element is less than 64 bytes, use ziplist encoding, otherwise use skiplist (skip table) encoding

Reasonable threading model

Single-threaded model: avoids context switching

Redis is single-threaded, which actually means that Redis network IO and key-value pair reading and writing are done by one thread. But other functions of Redis, such as persistence, asynchronous deletion, cluster data synchronization, etc., are actually performed by additional threads.

The single-threaded model of Redis avoids unnecessary context switching of the CPU and the consumption of competing locks . Because it is a single thread, if a command is executed for too long (such as the hgetall command), it will cause blocking. Redis is an in-memory database for fast execution scenarios, so commands such as lrange, smembers, and hgetall should be used with caution.

What is context switching ? for example:

For example, you are reading an English novel, you see a certain page, you find a word that you cannot read, you add a bookmark, and then go to the dictionary. After looking up the dictionary, you come back and start reading from the bookmark, and the process is very comfortable.

If you're reading this book alone, you'll be fine. But if you go to the dictionary and someone else flips through your book and slips away. When you come back and find that the book is not the page you were looking at, you have to take the time to find your page.

For a book, it’s fine if you look at it and label it by yourself, but if there are many people flipping through it, the various labels of the book will be messy. Maybe this explanation is rough, but the truth should be the same.

I/O multiplexing

What is I/O multiplexing?

  • I / O: Internet I / O
  • Multiplex: Multiple network connections
  • Reuse: Reuse the same thread.
  • IO multiplexing is actually a synchronous IO model, which enables a thread to monitor multiple file handles; once a file handle is ready, it can notify the application to perform corresponding read and write operations; and when no file handle is ready , it will block the application and hand over the cpu.

Multiple I/O multiplexing technology allows a single thread to efficiently process multiple connection requests, and Redis uses epoll as the implementation of I/O multiplexing technology. And Redis's own event processing model converts connections, reads and writes, and closes in epoll into events, without wasting too much time on network I/O.

virtual memory mechanism

Redis directly builds the VM mechanism by itself. It will not call system functions for processing like a general system, and it will waste a certain amount of time to move and request.

What is the virtual memory mechanism of Redis?

The virtual memory mechanism is to temporarily swap infrequently accessed data (cold data) from memory to disk, thereby freeing up valuable memory space for other data that needs to be accessed (hot data). Through the VM function, hot and cold data can be separated, so that hot data is still in memory and cold data is saved to disk. In this way, the problem of access speed degradation due to insufficient memory can be avoided.

Guess you like

Origin blog.csdn.net/wdjnb/article/details/124273170