Wenyuan network, only for the use of learning, if there is infringement please contact delete.

The use of caching is an evolutionary process.

Ask yourself, what is the most immediate reason to use caching?

Nothing but speed!

Looking back to my initial use of cache, some database stored unchanged configuration information, when the service is started, directly loaded to the local public module, convenient for other functional modules to share. This is the most basic and simple local caching application.

Services and caching

The so-called service, in short, is a layer of application and a layer of data, from which the application gets the data and processes the output.

In the data layer, we usually refer to persistent storage on persistent media. It can take many forms, be a file, or a database.

The data is stored on persistent media while the application runs in memory. Memory and persistence media are two different media with an order of magnitude speed difference, so there is a “contradiction” between application and data.

With this paradox in mind, there is an urgent need for caching.

When we say cache, it must be stored in memory, so that it can be closer to the application, faster to provide the data that the application needs, for faster service response.

Of course, the cache does not completely isolate the persistence layer data. Caching, there’s a word that comes with it called hit ratio.

When the data we query exists in the cache, we call it a “hit”, and the required data can be provided directly by the cache.

For the data that is not “hit”, it needs to go through the cache layer to further depersist the data layer retrieval. This scenario is called cache penetration.

Once the data is retrieved, we need to refill the cache for the next “hit” query before returning it to the application.

Of course, we only refer to the “read” query scenario.

When data manipulation changes are made to the application, we need to update the changes to both the persistence layer and the buffer layer. At this point, we will be faced with another problem, “before” and “after” problem.

The problem of “first” and “last” is also called the cache consistency problem.

If the cache is updated first, the persistence layer may fail to update, resulting in dirty data in the cache.

However, if the persistence layer is updated first, we have to face the dilemma that the cache will provide old data to the outside world between the time the persistence layer is successfully updated and the time the cache is updated.

The problem of cache consistency, especially in high-concurrency environments, requires more sophisticated control for specific scenarios.

For example, consistent locks for concurrent modifications; For example, delayed refreshing of asynchronous refreshes and so on.

Cache and update

We mentioned the consistency of cache update above, which can be subdivided into strong consistency requirements, weak consistency requirements and final consistency requirements in terms of practical application scenarios.

1. Strong consistency requirements

For example, applications such as transaction status information, placed order, payment in progress and paid require us to take the initiative to update the association and ensure consistency at the transaction level.

Many theories, including distributed transaction, have also provided a good practice scheme for us to solve practical problems.

2. Weak consistency requirements

Some involve less important information updates and can tolerate scenarios where persistent data and cache data are inconsistent for a short period of time (say, a few minutes). For example, non-explicit description information, statistical count cache information, etc. It is usually possible to do this asynchronously.

Some scenarios that output fixed information over a short period of time (seconds, minutes). For example, update hotspot information and ticket price information every 30 seconds. This can be handled by setting the cache timeout to be automatically culled.

3. Final consistency requirements

Ensure final consistency of data state.

Third, cache granularity

The so-called granularity, that is, cache information block level, size. The choice of granularity of cache depends on the overall architecture of our application, data storage planning, and specific application scenarios.

Take user information for example, cache active information? Or is it relatively static information? Do you cache on a single attribute hierarchy? Or the whole object?

Different data granularity also determines how we store the cache: binary serialized data for the entire object? More transparent JSON strings? One-to-one mapping of attributes to values?

Each format has its own advantages and disadvantages, and developers can evaluate the options comprehensively in terms of application, storage and maintenance costs.

4. Harm of cache penetration

In the first section, we talked about why cache penetration occurs: a cache miss. So why did it miss?

1. Data does not exist in the cache temporarily

A temporary application can be loaded to the cache according to the initial load, lazy load on demand.

Or cache data can be automatically or actively expired by our specific cache expiration policies, which typically include element limits, memory usage limits, and lifetime limits.

In fact, whether the initial load is not loaded or the cache expires, delete, these are all assumed to be normal application scenarios, again we won’t comment too much.

2. Data never exists

When a request for nonexistent data comes in, it will inevitably cross the cache and reach the persistent storage layer.

The response capacity of persistent storage is limited, and when such requests reach a certain level, the service may face the risk of downtime.

At this point, our understanding of the role of caching also needs to be extended to reduce the underlying load and protect backend resources.

The reasons for such cache penetration can be simply divided into internal and external causes: internal application logic problems, external malicious attacks, crawler interference and so on.

Internal problems are easy to solve, insight is predictable, benign optimization can be;

On the contrary, it is the external unpredictability that may require more cautious defensive treatment.

In fact, there is only one thing to deal with at the cache level, both internally and externally: effective interception penetration.

At this point, the conventional thinking first step is to put the data that caused the cache penetration into the cache, whether it exists in persistent storage or not.

For example, for normal deleted user data, soft deletion at the cache level is carried out to mark with status information (I exist, in fact, I do not exist!). . The penetrating pressure caused by such problems can be well solved.

However, we have a clear understanding that the real harm can be caused by abnormal intrusion data. For example, if all traversal data is exhausted and cached one by one, the only result is that the cache resources overflow. It’s a pretty scary scenario.

For this “big data” type of attack, Bloom filtering interception may be a good choice.

5. Also cache avalanche

In the previous section, we talked about the load-protection capabilities of the cache, which can be used to quickly respond and to load-protect persistent data.

In some read-oriented services, the cache carries almost 90% or more of the requests.

However, if the cache is temporarily out of service for some reason, all requests will penetrate into the persistent storage tier, causing extreme outages of the storage tier.

So what should we do about it?

1. High availability

High availability of cache is the primary guarantee against cache avalanche: primary/secondary, read/write separation, dynamic capacity expansion, consistency balancing, and remote DISASTER recovery.

Practical applications such as Redis sentinel mode, cluster deployment and so on.

2. Limiting the flow of service governance, fuse downgrading

What is the purpose of service governance? Service stability.

Current limiting is the control of abnormal flow; Protection of core service resources of fusible and degraded targets.

The author has introduced the use of several popular flow control frameworks in lightweight fusing downgrading framework Alibaba Sentinel application.

Caches and persistent datastores are both resources, or we can deal with a cache avalanche scenario by fusing and downprotecting the cache and persisting datastores.

I’ve compiled the interview questions and answers in PDF files, as well as a set of learning materials covering, but not limited to, the Java Virtual Machine, the Spring framework, Java threads, data structures, design patterns and more.

Follow the public account “Java Circle” for information, as well as quality articles delivered daily.