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

As we know, Redis provides high-performance data access functions, so it is widely used in cache scenarios, which can effectively improve the response speed of business applications and avoid sending requests with high concurrency and great pressure to the database layer.

However, if there is a problem when Redis does cache, such as cache failure, then a large number of requests will be directly backlogged to the database layer, which is bound to bring great pressure to the database, is likely to lead to database downtime or failure, so the business application has no way to access data and respond to user requests. This kind of production accident is certainly not what we want to see.

Because of the ubiquity of Redis as a cache and its important role in business applications, we need to systematically understand a number of aspects of the cache, including how it works, replacement strategies, exception handling, and extension mechanisms. Specifically, we need to address four key issues:

  • How exactly does Redis cache work?
  • What if the Redis cache is full?
  • Why are there exceptions such as cache consistency, cache penetration, cache avalanche, and cache breakdown?
  • After all, Redis memory is limited, if using fast SOLID-state drive to store data, can increase the amount of data cached, then Redis cache can use fast solid-state drive?

In this lesson, we will take a look at the characteristics of the cache, the natural advantages of Redis for caching, and how the Redis cache works.

Characteristics of caching

To understand why Redis is suitable for caching, we need to understand the characteristics of caching.

First of all, you need to know that different layers of a system have different access speeds, which is why we need caching, so we can put some data that needs to be accessed frequently in the cache to speed up their access.

To give you a better idea, let me take a computer system as an example. The following figure shows the three tier storage structures in a computer system, along with their common capacity and access performance. The processor is at the top, the memory is in the middle, and the disk is at the bottom.

As can be seen from the figure, the access speed of the three layers of CPU, memory, and disk varies greatly from tens of ns to 100ns, and then to several ms.

Imagine if every time the CPU processed data, it had to read data from a slow MS disk and then process it. The CPU would have to wait for the data transfer to complete. As a result, the fast CPU is weighed down by the slow disk, and the entire computer system runs very slowly.

So, by default, there are two kinds of caches in a computer system:

  • The last level cache in the CPU, LLC, is used to cache the data in memory, so that data is not accessed from memory every time.
  • The high speed page cache, or page cache, is used to cache data on disk instead of fetching data from disk each time.

LLC access is faster than memory, and memory access is faster than disk access. So, we can see the first characteristic of the cache: in a hierarchical system, the cache must be a fast subsystem, avoiding the need to retrieve data from the slow subsystem every time the data is stored in the cache. Corresponding to the Internet application, Redis is a fast subsystem, and the database is a slow subsystem.

With this in mind, you can understand why we have to do everything we can to make Redis provide high-performance access, because if access is slow, Redis is of little value as a cache.

Let’s go back to the computer hierarchy. The LLC size is at the MB level, page cache size is at the GB level, and disk size is at the TB level. This brings up the second feature of caching: the capacity of a caching system is always smaller than that of a slow back-end system, and it is impossible to store all data in a caching system.

This is interesting because it shows that the capacity of the cache is limited, the amount of data in the cache is limited, and there is no way to meet the demand for access all the time. Therefore, there must be an interaction between the cache and the back-end slow system, which involves writing back and re-reading data. In simple terms, the data in the cache needs to be flushed out according to certain rules and written back to the back-end system, and the new data needs to be read in from the back-end system and written to the cache.

In fact, this is one of the important reasons why Redis is suitable for caching.

Now that we know two important features of caching, let’s look at how caching handles requests. In practice, business applications access data in the Redis cache without the data necessarily being there and, therefore, are handled differently.

The Redis cache handles both cases of a request

When Redis is used as a cache, we will deploy Redis in the front of the database. When business applications access data, they will first query whether the corresponding data is saved in Redis. At this point, there are two scenarios depending on whether the data is in the cache or not.

  • Cache hit: Redis has the corresponding data, directly read Redis, performance is very fast.
  • Missing cache: Data is read from the back-end database without being stored in Redis, and performance slows. Also, once a cache miss occurs, we need to write the missing data to Redis in order for subsequent requests to be able to read from the cache, a process called cache update. The cache update operation involves ensuring data consistency between the cache and the database, which I’ll discuss in lecture 25.

I’ve drawn a picture that clearly shows how the application reads data when a cache hit or miss occurs, and you can take a look at this picture.

Suppose we use Redis as a cache in a Web application. User requests are sent to Tomcat, which handles the business logic. To access data, you need to read and write data from MySQL. So, we can deploy Redis in front of MySQL. If the accessed data is in Redis and the cache hits, Tomcat can read the data directly from Redis to speed up application access. Otherwise, Tomcat would need to read data from a slow database.

As you may have noticed by now, there are basically three operations we do with Redis cache:

  • When an application reads data, it needs to read Redis first.
  • When a cache miss occurs, data needs to be read from the database;
  • When a cache miss occurs, you also need to update the cache.

So, who exactly does this? This is related to how Redis cache is used. Next, I’ll talk to you about how Redis works as a bypass cache.

Redis is used as the bypass cache

Redis is a separate system software, and business applications are two different software. When we deploy the Redis instance, it will only passively wait for the client to send the request, and then process it. Therefore, if the application wants to use Redis cache, we need to add the corresponding cache operation code in the application. Therefore, we also refer to Redis as the bypass cache, which means that reading the cache, reading the database, and updating the cache are all done in the application.

This is not the same as LLC and Page cache in computer systems as I mentioned earlier. As you may recall, we didn’t explicitly create instances of LLC or Page cache in our code or explicitly call their GET interfaces when we were developing programs. This is because when we build our computer hardware systems, LLC and page cache are already in the application’s data access path, so the application can use the cache directly when accessing the data.

So, when using the Redis cache, specifically, we need to add three areas of code to our application:

  • When the application needs to read data, we need to explicitly call Redis GET operation interface in the code, query;
  • If the cache is missing, the application needs to connect to the database again to read data from the database.
  • When data in the cache needs to be updated, we also need to explicitly call the SET operation interface in the application to write the updated data to the cache.

So, how do you add the code? Let me show you an example of pseudo-code that uses Redis caching in a Web application.

String cacheKey = "productid_11010003"; String cacheValue = rediscache.get (cacheKey); // Cache hit if (cacheValue! = NULL) return cacheValue; Else cacheValue = getProductFromDB(); Rediscache. put(cacheValue) // Cache updateCopy the code

As you can see, in order to use the cache, the Web application needs to have an instance object representing the cache system, redisCache, actively invoke the GET interface of Redis, and handle the logic of cache hits and cache misses, such as the need to update the cache in case of a cache miss.

With this in mind, there is one thing to watch out for when using the Redis cache: Because of the need to add code to use the cache, so, Redis does not apply to those who don’t have access to the source code of the application, some long before the development of applications, for example, already no longer maintain their source code, or the application of the third party supplier development, does not provide the source code, so we have no way of caching operations in these applications.

When using the bypass cache, we need to add operation code in the application, which increases the extra work of using Redis cache. However, because Redis is a bypass cache and is an independent system, we can independently expand or optimize the Redis cache. And as long as the interface stays the same, the code we add to the application doesn’t need to be changed.

Ok, so now we know that by adding Redis operation code to our application, we can make our application use Redis to cache data. However, in addition to querying and reading data from the Redis cache, the application may modify the data. In this case, we can modify the data in the cache or in the back-end database. How do we choose?

In fact, there are two types of Redis caches: read-only caches and read-write caches. A read-only cache speeds up read requests, while a read-write cache speeds up both. Furthermore, the read/write cache has two data write back strategies, allowing us to choose between ensuring performance and ensuring data reliability based on business requirements. So, next, let’s take a look at the Redis cache type and corresponding write back strategy.

Type of cache

Depending on whether the Redis cache accepts write requests, we can divide it into read-only cache and read-write cache. Let’s take a look at read-only caching.

A read-only cache

When Redis is used as a read-only cache, the application will first call the Redis GET interface to check whether the data exists. All data write requests are sent directly to the back-end database, where additions, deletions and changes are made. For deleted data, if Redis has cached the corresponding data, the application needs to delete the cached data, Redis does not have the data.

When the application reads the data again, a cache miss occurs, and the application reads the data from the database and writes it to the cache. In this way, the data can be retrieved directly from the cache when it is read later, which speeds up access.

Let me give you an example. If A business application wants to modify data A and data A is cached in Redis, the application will modify data A directly in the database and delete data A from Redis. When the application needs to read data A, A cache miss will occur. At this point, the application reads A from the database and writes to Redis for subsequent requests to read directly from the cache, as shown in the figure below:

Read-only caches have the advantage of updating data directly in the database. All the latest data is in the database, and the database provides data reliability assurance, so there is no risk of data loss. This type is used when we need to cache images, short videos, and other user-only data.

Read and write cache

With read-only caches in mind, read and write caches are easy to understand.

For the read/write cache, all write requests are also sent to the cache, where data is added, deleted, or modified. At this time, thanks to the high performance access feature of Redis, the operation of adding, deleting and modifying data can be completed quickly in the cache, and the processing result can be quickly returned to the business application, which can improve the response speed of the business application.

However, unlike read-only caches, when using read/write caches, the most recent data is in Redis, which is an in-memory database that can be lost in the event of a power failure or outage. This means that the latest data of the application may be lost, putting the application business at risk.

Therefore, there are synchronous direct write and asynchronous write back policies according to different requirements of business applications on data reliability and cache performance. The synchronous direct write policy preferentially ensures data reliability, while the asynchronous write back policy preferentially provides fast response. Learning to understand these two strategies can help you make the right design choices based on your business needs.

Next, let’s look at these two strategies in detail.

Synchronous direct write means that a write request is sent to the cache and the back-end database for processing. After data is written to both the cache and database, the write request is returned to the client. This way, even if the cache goes down or fails, the latest data is kept in the database, which provides data reliability.

However, synchronous direct write degrades cache access performance. This is because write requests are processed fast in the cache and slow in the database. Even if the cache processes the write requests quickly, it will have to wait for the database to process all the write requests before returning the results to the application, which increases the cache response latency.

The asynchronous write back strategy prioritizes response latency. At this point, all write requests are processed in the cache first. When the modified data is ready to be flushed from the cache, the cache writes it back to the back-end database. In this way, processing of the data is done in the cache and can be done quickly. However, if there is a power failure and they have not been written back to the database, they are at risk of being lost.

To make it easier for you to understand, I also drew the following picture, which you can take a look at.

The choice between read-only cache and read-write cache depends on whether we need to speed up write requests.

  • If we need to speed up write requests, we choose read/write caching;
  • If there are few write requests, or if we just need to speed up the response to read requests, we choose read-only cache.

For example, in the scenario of a mass sale, the inventory information of an item is constantly being modified. If every change has to be processed in the database, it will slow down the entire application, so we usually choose the read/write caching mode. However, in the scenario of short video App, although there are many video attributes, they are generally not frequently modified after confirmation. In this case, modification in the database has little impact on cache, so read-only cache mode is a suitable choice.

summary

Today, we learned about two characteristics of caching: in a hierarchical system, data ephemeral in a fast subsystem helps speed access; The cache capacity is limited. When the cache is full, data needs to be discarded. Redis, with its high performance access and data flushing mechanisms, is a good fit for caching.

In addition, we learned about Redis’s ability to act as a bypass cache, which means that you need to add code to your application that handles the cache logic. Of course, if you cannot modify the source code, you cannot use Redis for caching.

When Redis does cache, there are two modes, namely read-only cache and read/write cache. Read/write cache also provides synchronous direct write and asynchronous write back modes. The synchronous direct write mode focuses on ensuring data reliability, while the asynchronous write back mode focuses on providing low-latency access. You need to select the synchronous direct write mode based on actual service scenarios.

In this class, ALTHOUGH I mentioned that Redis has a data elimination mechanism, I did not elaborate on the specific elimination strategy. So how exactly does Redis weed out data? I’ll give you more details next time.

Each lesson asking

As usual, LET me give you a quick question. In this lesson, I mentioned the Redis read-only cache and the read/write cache using the direct write strategy. Both caches synchronize data to the back-end database. Do you think there is any difference between them?

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/or colleagues. I’ll see you next time.