In the previous article, we mentioned that in May 2020, Redis officially launched the impressive Redis 6.0, which proposed many new features, including Client side caching, ACL, Threaded I/O and Redis Cluster Proxy, etc. Many new features. As follows:
We have also made a detailed description of the Threaded I/O (multi-threaded network I/O mode) of Redis 6.0, and if you are interested, turn to the previous one.
In this article, let's talk about Client side caching, and see why Redis needs client side caching, what principle it is based on, and how it should be used.
1 Why client-side caching is needed
1.1 Purpose of the cache service
Recalling what we said in the first article "Understanding the Essence of High-Performance Redis", Redis's read and write operations are all implemented in memory, compared to other persistent storage (such as MySQL, File, etc., data persistence on disk), the performance will be much higher. Because when we operate data, we need to read the data into memory first through IO operations, which increases the work cost.
The picture above comes from the Internet. You can look at his pyramid model. The higher the execution efficiency, the more expensive the price. The execution time comparison of each layer is given below:
Let's take an intuitive comparison between L1 and SSD. If L1 takes 1s, it will take about 15 to 45 hours in SSD, so the access efficiency at the memory level is much higher than that at the disk level.
In short, the purpose of caching is to improve efficiency based on efficient access to data (such as MySQL data, file data, etc.) persisted on disk. It is also mentioned in "Redis in Action" that Redis can improve the performance of ordinary relational databases by 10 to 100 times.
The data access process is shown in the figure below. Redis stores hot data. When we request a data that day, we first access the cache layer. If it does not exist, we access the database. This can solve most of the business scenarios of efficiently reading data. The performance is the highest in the cache. one of the most important values.
1.2 Existing problems
Although we use Redis to improve the efficiency of data access, there are still some problems. The cache service based on distributed access is an independent service. Generally, accessing it requires the following steps:
Connect to the cache service (generally not on the same instance as the computing service)
Find and read data (I/O operations)
Data serialization and deserialization also
affect performance. With the development of the Internet and the continuous expansion of traffic, it is easy to reach the performance limit of Redis.
Therefore, we often use the process cache (local cache) to assist processing, and temporarily store some data that is frequently read and written at low frequency locally. When reading data, first check whether the local cache exists, and then access the remote cache if it does not exist. service data to further improve access efficiency.
If Redis does not exist, you can only query in the database, and then set the found data to Redis and the local cache, so that subsequent requests do not need to go to the database.
Generally, we will use Memcached, Guava Cache, etc. as the first-level cache (local cache), and use Redis as the second-level cache (cache service). Local memory avoids operations such as connection, query, network transmission, and serialization. Cache service is much faster, this mode greatly reduces data latency.
2 Client cache implementation principle
Redis itself implements a client-side cache to assist the operation of server-side Redis, called tracking.
We can configure it with the command:
CLIENT TRACKING ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]
The core problem of client-side caching is that when the cache in Redis changes or becomes invalid, if the client-side cache can be notified in a timely and effective manner to ensure data consistency.
Redis 6.0 implements the Tracking function, which provides two solutions to ensure data consistency:
2.1 Normal Mode
Redis uses TrackingTable to store client data in normal mode, and its data type is radix tree.
radix tree is a multi-fork search tree for sparse long integer data search. It can complete the mapping quickly and save space. If you want to know more, you can read this introduction.
As shown in the figure, the client ID list has a mapping relationship with the pointer of the Redis storage key. The pointer of the Redis key object corresponds to the memory address, and the data structure is Long.
When the track function is enabled, the operation has the following characteristics:
When Redis obtains a key value information, radix tree will call the enableTracking method to record the mapping relationship between key and clientId and record it in TrackingTable.
When Redis deletes or modifies a key-value information
The radix tree calls the trackingInvalidateKey method according to the key to find the corresponding Clinet ID
Call the sendTrackingMessage method to send invalid key-value information (invalidate message) to these Clinet IDs.
Delete the mapping relationship from TrackingTable after sending.
After the client disables the track function, when encountering a large number of delete operations, it is generally lazy to delete, and only the CLIENT_TRACKING flag is deleted.
The default track mode is not enabled, it needs to be enabled through the command, refer to the following:
CLIENT TRACKING ON|OFF
2.2 Broadcast Mode (BCAST)
The broadcast mode is similar to the normal mode, and the mapping relationship is used for comparison, but the implementation process is still different:
The stored content is different: as shown in the figure, the Prefix Table is used to store the client data, which stores the mapping relationship between the prefix string pointer and the client data (client ID list + key value list to be notified) .
The timing of deleting key-values varies:
The radix tree calls the trackingInvalidateKey method according to the key to find the PrefixTable.
To judge whether it is empty or not, call trackingRememberKeyToBroadcast to traverse the key list, find the ones that meet the prefix matching rules, and record the location.
Call the trackingBroadcastInvalidationMessages function in the event processing cycle function beforeSleep to send the message.
Delete the mapping relationship from the PrefixTable after sending.
2.3 Forwarding Mode
The RESP 3 protocol is a newly enabled protocol in Redis 6.0. The use of normal mode or broadcast mode needs to rely on this protocol, which will cause problems for clients of the RESP 2 protocol. So derived in addition to another mode: redirection (redirect).
RESP 2 cannot directly PUSH the invalidation message, so it cannot directly obtain the invalidation data (Redis Client 2).
The client supporting the RESP 3 protocol (Redis Clinet 1) tells the Server to notify the RESP 2 client of the failure message through Pus/Sub.
And Redis Client 2 (RESP 2) subscribes to the channel redis :invalidate for sending invalid messages through the subscription command SUBSCRIBE .
# Redis Client 2 (supports RESP 2) execute subscription
client id : 888
# Redis Client 1 (support RESP 3), forward to 2
client tracking on bcast redirect 888
3.1 Default Mode (Normal Mode)
The server records the keys operated by the client. When the value corresponding to the key changes, it will send Invalidation Messages to the Redis client.
Recording key information on the server consumes some memory, but the scope of sending invalidation messages is limited to the range of stored keys, and computation and network transmission become lighter.
The advantage is to save CPU and traffic bandwidth, but it will take up some memory.
3.2 Broadcast Mode
The server does not record the key, but subscribes to the specific prefix of the key. When the value of the key matching the prefix changes, it sends Invalidation Messages to the Redis client.
The advantage is that the server consumes less memory, but consumes more CPU for prefix matching calculations.
3.3 Forwarding Mode