This is the 11th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

The use of Redis cache greatly improves the performance and efficiency of the application, especially in terms of data query. But at the same time, it also brings some problems. Among them, the most crucial problem, it is the consistency problem of data, strictly speaking, this problem has no solution. If data consistency is a high requirement, then caching is not an option.

We can only adopt appropriate strategies to reduce the probability of data inconsistency between the cache and the database, but cannot guarantee strong consistency between the two. Appropriate policies include appropriate cache update policies, updating the cache promptly after updating the database, and adding a retry mechanism when caching fails, such as MQ mode message queues.

Typical problems: Cache penetration, cache avalanche, cache breakdown

The cache to penetrate

The data (key) for which a large number of concurrent requests are made by the user does not exist in either the Redis or the persistence layer database, resulting in each query to the persistence layer database even though the data does not exist.

For the sake of fault tolerance, data will not be written to the cache if it cannot be retrieved from the persistence layer. Cache penetration will result in nonexistent data being queried at the persistence layer on every request, losing the meaning of cache protection for backend persistence.

Schematic diagram:

The solution

1. Basic check

Add authentication at the interface layer, for example, user authentication authentication. The basic authentication is performed by ID. If id<=0, interception is performed directly.

2. Cache empty objects

Set (key,null) to the key if the persistence layer does not hit

There is a problem
  1. Memory space occupied: Null value does not mean that no memory space is occupied. The null value is cached, which means that more keys are stored in the cache layer, requiring more memory space. A more effective method is to set a short expiration time for such data, so that it can be automatically eliminated.

  2. Inconsistent data: Data at the cache layer and storage layer are inconsistent for a period of time, which may affect services.

    For example, if the expiration time is set to 5 minutes, if this data is added to the storage layer, then the cache layer and the storage layer data will be inconsistent during this period of time. In this case, the message system or other means can be used to clear the empty objects in the cache layer.

3. Bloom filter

Before accessing the cache layer and storage layer, the bloom filter is used to save the existing key in advance for the first layer interception. When receiving a request for key, the Bloom filter is used to verify whether the key exists. If it exists in the cache layer and storage layer.

You can use bitmap as a bloom filter. This method is applicable to the application scenarios with low data hits, relatively fixed data, and low real-time performance, complicated code maintenance, but less cache space usage.

The Bloom filter is actually a long binary vector and a series of random mapping functions. A Bloom filter can be used to retrieve whether an element is in a collection. Its advantages are that the space efficiency and query time are far more than the general algorithm, but its disadvantages are that it has a certain rate of misidentification and difficult to delete.

Algorithm description:

  • In its initial state, BloomFilter is an array of bits of length m, with each bit set to 0.
  • When adding element x, k hash functions are used for x to obtain K hash values, mod m, and set the corresponding bit to 1.
  • To determine whether y belongs to this set, k hash functions are used for y to get K hash values, mod m, all corresponding positions are 1, then y is considered to belong to this set (hash conflict, misjudgment may exist), otherwise y is not considered to belong to this set. You can reduce the error rate by increasing the hash function and increasing the length of the binary bit array.
There is a problem
  1. There is misidentification;
  2. Deletion is difficult.

Cache breakdown

When a key is so popular that it is constantly carrying a large number of concurrent accesses, and when the key fails at the moment, the continuous large number of concurrent accesses penetrates the cache and directly requests the database, as if a hole is punched in the barrier.

Key invalidation: When a key cache expires, the database is accessed to query the latest data and the cache is written back.

The solution

1. Set the hotspot key to never expire
  • From the cache level, it is true that there is no set expiration time, so there is no problem that occurs after the hot key expires, that is, the “physical” does not expire.
2. Distributed mutex

Only one thread is allowed to rebuild the cache. Other threads can wait for the rebuilding thread to finish and then retrieve data from the cache again. set(key,value,timeout)

Comparison of the two schemes:

  • Set the hotspot to never expire: duekeyThere is no expiration time. It virtually no longer existsHot keyA series of problems have arisen. However, there will be data inconsistency, and the code complexity will increase.
  • Distributed mutex: there are some risks, if the query database + rebuild the cache time process, there may be a risk of deadlock and thread pool blocking, high concurrency scenarios throughput will be greatly reduced! But this method can better reduce the back-end storage load, and in the consistency of better.

Cache avalanche

The cache layer is unavailable (down) or a large number of caches fail within the same period of time (a large number of keys fail or hotspot data fail). A large number of requests directly reach the storage layer and the storage layer is overloaded, causing an avalanche.

The solution

1. Stagger the failure time of key

The expiration time of cached data is set randomly to prevent a large number of data from being expired at the same time.

2. High availability

The caching layer can be designed to be highly available, providing service even if individual nodes, individual machines, or even machine rooms go down. Sentinel or cluster;

3. Multi-level cache

Multiple levels of caching are adopted, with the local process as the first-level cache and Redis as the second-level cache. Different levels of caching have different timeouts. Even if one level of caching expires, other levels of caching will also be used.

Cache warming

Cache warm-up is to load the relevant cache data directly into the cache system after the system goes online.

This avoids the problem of querying the database and then caching the data when the user requests it! The user directly queries the preheated cache data!

Cache the drop

When traffic surges, services become problematic (such as slow or unresponsive response times), or non-core services affect the performance of core processes, you still need to ensure that the service is available, even if it is lossy. The system can perform automatic degradation based on key data or configure switches to perform manual degradation.

The ultimate goal of cache degradation is to ensure that core services are available, even if they are lossy. And there are some services that can’t be downgraded (like adding to the cart, billing).