“This is the 25th day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”

Redis officially released version 6.0 in May this year, which has many new features. Therefore, 6.0 has just been launched, the industry has been widely concerned.

So, at the end of the course, I arranged this session to talk to you about some of the key new features in Redis 6.0: multi-IO threading for network processing, client caching, fine-grained permission control, and the use of the RESP 3 protocol.

Among them, the multi-IO thread oriented to network processing can improve the speed of network request processing, and the client cache can let the application directly read data locally on the client, which can improve the performance of Redis. In addition, fine-grained permission control enables Redis to control the access rights of different users according to the command granularity, enhancing Redis security protection. The RESP 3 protocol enhances the functionality of the client, making it easier for applications to use the different data types of Redis.

Once you understand how these features work, you’ll be better able to decide whether to use version 6.0. If you are already using 6.0, you can also find out how to use it better and less potholes.

First, let’s take a look at the new multithreading feature in version 6.0.

From single-threaded processing of network requests to multi-threaded processing

The first new feature that got a lot of attention in Redis 6.0 was multithreading. This is because Redis has long been known for its single-threaded architecture. Although some command operations can be performed by background threads or sub-processes (such as data deletion, snapshot generation, AOF rewriting), everything from network IO processing to the actual read and write command processing is done by a single thread.

As the performance of network hardware improves, Redis performance bottlenecks sometimes occur in the processing of network IO, that is, the speed at which a single main thread can process network requests cannot keep up with the speed of the underlying network hardware.

There are two ways to deal with this problem.

The first approach is to replace the kernel network protocol stack with a user-mode network protocol stack (such as DPDK), so that the processing of network requests is done directly in the user-mode rather than in the kernel.

For high performance Redis, avoiding frequent network request processing by the kernel can greatly improve the efficiency of request processing. However, this approach requires the addition of support for user-mode network protocol stack in the overall architecture of Redis, and the modification of Redis source code related to the network (such as the modification of all network sending and receiving request functions), which will bring a lot of development work. And the new code may introduce new bugs, causing system instability. Therefore, this approach is not used in Redis 6.0.

The second method is to use multiple IO threads to process network requests to improve the parallelism of network request processing. Redis 6.0 takes this approach.

However, Redis’s multi-IO threads are only used to handle network requests. Redis still uses single threads for read and write commands. This is because network processing is often a bottleneck when Redis processes requests. Having multiple I/O threads process network operations in parallel can improve the overall processing performance of an instance. By continuing to use a single thread to execute commands, you don’t need to develop additional multithreaded mutex mechanisms to keep Lua scripts and transactions atomistic. In this way, the Redis threading model is easy to implement.

Let’s take a look at how the main thread and the IO thread work together to complete request processing in Redis 6.0. You can’t really use multithreading until you know how it works. To help you understand, we can divide the main thread and multi-IO thread collaboration into four phases.

Phase 1: The server establishes a Socket connection with the client and allocates processing threads

First, the main thread is responsible for receiving the request to establish a connection. When a client requests a Socket connection to an instance, the main thread creates a connection to the client and places the Socket in a global wait queue. Next, the main thread allocates Socket connections to the IO thread through polling.

Phase 2: The IO thread reads and parses the request

Once the main thread allocates the Socket to the IO thread, it blocks and waits for the IO thread to finish reading and parsing the client request. Because there are multiple IO threads working in parallel, this process can be completed quickly.

Phase 3: The main thread performs the request operation

After the IO thread has parsed the request, the main thread will still execute these commands in a single-threaded fashion. The diagram below shows the three stages just described, so you can take a look at them and understand them better.

Phase 4: The IO thread writes back to the Socket and the main thread clears the global queue

When the main thread completes the request, it writes the result to the buffer. The main thread then blocks and waits for the I/O thread to write the result back to the Socket and return it to the client.

Just as the IO thread reads and parses the request, the IO thread writes back to the Socket with multiple threads running concurrently, so the writing back to the Socket is also fast. After the IO thread writes back to the Socket, the main thread clears the global queue and waits for further requests from the client.

I’ve also drawn a picture of what the main thread and the IO thread are doing at this stage, so you can take a look.

Now that we know how Redis main thread and multithreading work together, how do we enable multithreading? In Redis 6.0, multi-threading is disabled by default. If you want to use multi-threading, you need to complete two Settings in redis.conf.

1. Set the IO -thread-do-reads configuration item to yes, indicating that multi-threading is enabled.

io-threads-do-reads yes
Copy the code

2. Set the number of threads. In general, the number of threads should be smaller than the number of CPU cores on the machine where the Redis instance resides. For example, for an 8-core machine, Redis officially recommends 6 IO threads.

io-threads  6
Copy the code

If you find that the CPU overhead of the Redis instance is small but the throughput is not improved, you can consider using the multi-threading mechanism of Redis 6.0 to speed up the network processing and improve the throughput of the instance.

Implement server-side assisted client caching

An important addition to Redis 6.0 over previous releases is the implementation of server-side assisted client caching, also known as Tracking. With this function, the Redis client in the business application can cache the read data locally, and the application can read data quickly directly locally.

However, when caching data locally, we face a problem: if the data is modified or invalidated, how do we notify the client to invalidate the cached data?

Tracking in the 6.0 implementation implements two modes to solve this problem.

The first mode is the normal mode. In this mode, the instance logs the keys read by the client on the server side and monitors whether the keys have changed. When the key value changes, the server sends an invalidate message to the client to inform the client that the cache is invalid.

In normal mode, the server will only report the invalidate message to the recorded key once. This means that if the key is changed after the server sends the invalidate message to the client, the server will only report the invalidate message to the client once. The server will not send the invalidate message to the client again.

Only when the client executes the read command again, the server will monitor the read key again and send invalidate messages when the key changes. The design consideration is to save limited memory space. After all, if the client is no longer accessing the key, and the server is still logging key changes, memory resources will be wasted.

Tracking can be turned on or off in normal mode by executing the following command.

CLIENT TRACKING ON|OFF
Copy the code

The second mode is broadcast mode. In this mode, the server will broadcast the failure of all keys to the client. However, if the key is changed frequently, the server will send a large number of failure broadcast messages, which will consume a lot of network bandwidth resources.

Therefore, in practice, we will ask clients to register the prefix of the key we want to trace, and when the key with the registration prefix is changed, the server will broadcast the failure message to all registered clients. Unlike normal mode, in broadcast mode, the server notifies the client of key failure whenever it registers a key to be traced, even if the client has not read the key yet.

Let me give you an example of how a client can use broadcast mode to receive key failure messages. If the user: ID :1003 key is updated on the client, the client will receive an invalidate message.

CLIENT TRACKING ON BCAST PREFIX user
Copy the code

This monitoring of broadcast patterns for prefixed keys matches our naming conventions for keys. In practice, we will set the same business name prefix for the key under the same business, so we can use the broadcast mode very conveniently.

RESP 3 is the new communication protocol in 6.0. I will explain it to you later.

For clients using THE RESP 2 protocol, you need to use another mode, namely redirect. In redirection mode, a client that wants to receive notification of an invalid message needs to SUBSCRIBE to the channel _redis_:invalidate that sends the invalid message. At the same time, run the CLIENT TRACKING command on another CLIENT to set the server to forward invalid messages to clients using the RESP 2 protocol.

Let me give you another example of how to enable clients using THE RESP 2 protocol to accept failure messages. Assume that client B wants to obtain invalid messages, but client B supports only RESP 2, and client A supports RESP 3. We can SUBSCRIBE and CLIENT TRACKING on CLIENT B and A, respectively, as follows:

// CLIENT A executes CLIENT TRACKING ON BCAST REDIRECT 303 with the ID 303 SUBSCRIBE _redis_:invalidateCopy the code

If a key pair is modified, client B will receive an invalid message via the _redis_:invalidate channel.

Now that you know about client caching in version 6.0, let’s take a look at the third key feature, the Access Control List (ACL) feature for instances, which can effectively improve the security of Redis.

From simple password-based access to fine-grained permission control

Prior to Redis 6.0, secure access to an instance could only be controlled by setting a password. For example, a client needed to enter a password before connecting to the instance.

In addition, for high-risk commands (KEYS, FLUSHDB, FLUSHALL, etc.), prior to Redis 6.0, we could only rename these commands by renaming them so that the client could not call them directly.

Redis 6.0 provides more fine-grained access control in two main ways.

First, version 6.0 supports the creation of different users to use Redis. Before version 6.0, all clients could use the same password to log in, but there was no concept of user. In 6.0, we could use ACL SETUSER to create users. For example, we could create and enable a user normalUser with a password of “ABC” by executing the following command: normalUser

ACL SETUSER normaluser on > abc
Copy the code

In addition, version 6.0 also supports setting access to command actions on a user-grained basis. I have listed the specific operations in the table below, where the plus (+) and minus (-) signs indicate granting or revoking command call permission to the user, respectively.

Let me give you an example to help you understand. Assuming that we want to set the user normalUser to only invoke Hash command operations and not String command operations, we can run the following command:

ACL SETUSER normaluser +@hash -@string
Copy the code

In addition to setting the access control permission of a command or a command class, version 6.0 also supports setting the access permission by key.

This is done by using the tilde “~” and the prefix key to indicate the key that controls access. For example, we can set user normaluser to only perform command operations on keys prefixed with “user:” by executing the following command:

ACL SETUSER normaluser ~user:* +@all
Copy the code

There you have it: Redis 6.0 can set up different users to access instances, and it can set up the command actions that a user can or cannot perform on certain keys, depending on the granularity of the user and key.

In this way, we can easily and flexibly set different levels of command permissions for different users in multi-user Redis applications, which is very helpful in providing secure Redis access.

Enable RESP 3

Redis 6.0 implements the RESP 3 communication protocol, whereas RESP 2 was used previously. In RESP 2, the communication content between the client and server is encoded in the form of byte arrays. The client needs to decode the transmitted data according to the operation command or data type, which increases the complexity of client development.

RESP 3 directly supports discriminating encoding of multiple data types, including null values, floating point numbers, Booleans, ordered dictionary collections, unordered collections, and so on.

The so-called discriminant coding refers to distinguish different data types directly by different beginning characters. In this way, the client can directly determine the beginning character of the message to realize data conversion operations, which improves the client efficiency. RESP 3 also supports client caching in normal mode and broadcast mode.

summary

In this lesson, I introduced you to the new features of Redis 6.0. I put them together in a table that you can review and consolidate.

Finally, I’ll give you one more tip: Because Redis 6.0 is new, the new features need to be deployed and verified in real applications, so if you want to try Redis 6.0, you can try using Redis 6.0 for non-core business first, on the one hand, to verify the performance or functional benefits of the new features, on the other hand, It also prevents the core business from being affected by the instability of new features.

Each lesson asking

Which new Redis 6.0 feature or features do you think will help you?

Welcome to write down your thoughts and answers in the comments area, and we will exchange and discuss together. If you find today’s content helpful, you are welcome to share it with your friends and colleagues. I’ll see you next time.