Redis cache optimization Advanced -Redis

Here Insert Picture Description


Cache penetration

definition

A query data does not exist, will not hit the cache and DB, white prostitute buffer layer and DB. For fault tolerance considerations generally, finding out if the buffer layer data from the storage layer are written.

Cache penetration will lead to the absence of data each request should go to the storage layer to query, cache protection loses its meaning back-end storage.


the reason

The reason usually penetrate the cache as follows:

  • Issue its own business code or data appears.
  • Malicious attacks, causing a large number of reptiles and other hits empty

Cache penetrating solutions to problems

Null object cache

This is also well understood, it is noted that on an empty object, a set expiration time, the following pseudocode

String get(String key) {
	// 从缓存中获取数据
	String cacheValue = cache.get(key)

 	// 缓存为空
 	if (StringUtils.isBlank(cacheValue)) {
	 // 从存储中获取
	 String storageValue = storage.get(key);
	 cache.set(key, storageValue);
	 // 如果存储数据为空, 需要设置一个过期时间(300秒)
	 
	if (storageValue == null) {
 		cache.expire(key, 60 * 5);
	 }
	 return storageValue;
 } else {
	 // 缓存非空
	 return cacheValue;
 } 
 
}

Generally good enough, limiting case: malicious attacks, tens of millions of id does not exist, have hit the DB, you also have these tens of millions of id as the key, to keep the redis, though their value is empty , and you set an expiration time.

But you think about, you tens of millions of queries a DB, you also quite sad it, and you redis in which tens of millions cache key, Qibushibai white wasted precious memory resources. . . . .

So you need a Bloom filter.

Look at the scene, choice.


Bloom filter

For malicious attacks, a large number of requests to the server cache data does not exist due to penetration can also do first filtered through a Bloom filter, for Bloom filter data do not exist are generally able to filter out, do not let the request go down send back end.

When the Bloom filter returns a value exists, the value may not exist; when it say there, it certainly does not exist.

Here Insert Picture Description

Bloom filter is a large number of bits are not the same group and unbiased hash function.

Unbiased is called the hash value of the element can be calculated relatively uniform.

When the key is added to the Bloom filter, will be used for a plurality of hash function to the key hash value is then regarded as an integer index to the number of bits of length modulo operation to obtain a position, each hash functions will be considered a different location. Then the position of these bits are set to one group is completed add operation.

When asked whether there is key to the Bloom filter, like add, also the location of several hash are calculated to see if the number of bits in these locations are set to 1, as long as a bit is 0, then that Bloom filter in this key does not exist.

If you are one, this does not mean that this key must exist, but there is most likely because these bits are set to 1 may be due to the presence of other key due.

If this bit sparse group, this probability will be great, if this bit crowded group, this probability is reduced.

This method is not suitable for high data hit, relatively fixed data, real-time low (typically larger dataset) application scenarios, code maintenance more complex, but the cache takes up very little space .


Pseudo code

Carrying bag can guvua Bloom filter, introduced dependence

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>22.0</version>
</dependency>
import com.google.common.hash.BloomFilter;


//初始化布隆过滤器 

//1000:期望存入的数据个数,0.001:期望的误差率
BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf‐8")), 1000, 0.001);


//把所有数据存入布隆过滤器
void init(){
	for (String key: keys) {
       bloomFilter.put(key);
    } 
 }


String get(String key) {
	// 从布隆过滤器这一级缓存判断下key是否存在
	Boolean exist = bloomFilter.mightContain(key);
	if(!exist){
		return "";
	}
	// 从缓存中获取数据
	String cacheValue = cache.get(key);
	// 缓存为空
	if (StringUtils.isBlank(cacheValue)) {
		// 从存储中获取
		String storageValue = storage.get(key);
		cache.set(key, storageValue);
		// 如果存储数据为空, 需要设置一个过期时间(300秒)
		if (storageValue == null) {
		cache.expire(key, 60 * 5);
	 }
	 	return storageValue;
	 } else {
		 // 缓存非空
		 return cacheValue;
	 }
 }




Cache simultaneous failure

Due to the large quantities of cache at the same time failure could lead to a large number of simultaneous requests directly to penetrate the cache database, the database may cause momentary excessive stress or even hang.

Cache simultaneous failure Solution

In this case we increase the cache when the batch is the best these batch of data cache expiration time is set to a different time period of time . Such as a random time between 5-10 minutes.

Pseudo code

String get(String key) {
	// 从缓存中获取数据
	String cacheValue = cache.get(key);
	// 缓存为空
	if (StringUtils.isBlank(cacheValue)) {
		// 从存储中获取
		String storageValue = storage.get(key);
		cache.set(key, storageValue);
	   //设置一个过期时间(300到600之间的一个随机数)
		int expireTime = new Random().nextInt(300) + 300;
		if (storageValue == null) {
			cache.expire(key, expireTime);
		}
		return storageValue;
	} else {
		// 缓存非空
		return cacheValue;
	}
}

Cache avalanche

Cache avalanche refers to the caching layer can not hold back or shoot down , traffic will flee like the bison, like, playing to the back-end storage layer.

Since the buffer layer bearing a large number of requests, the effective protection of the storage layer, but if for some reason the buffer layer is unable to provide services ( such as large over complicated, can not support the buffer layer, or due to bad cache design, similar to the large number of requests to access bigkey , resulting in a sharp decline in the cache can support concurrent ), so a lot of requests will reach the storage layer, the call of the storage layer will surge, the storage layer will cause a cascade of downtime.


Cache avalanche solutions

Avalanche prevention and resolution cache problem, we can proceed from the following three aspects.

  • 1) ensuring high availability of services caching layer , such as the use Redis Sentinel or Redis Cluster.
  • 2) the need for isolated components is degraded and the rear end of the flow restrictor . Such as limiting the use of Hystrix downgrade components.
  • 3) ahead of the exercise . Before on-line project, after exercise caching layer dawdle out, applications and back-end load and the problems that may arise, on this basis, make some plans set.

Cache breakdown (hot key reconstruction buffer optimization)

Under normal circumstances, we use the "+ cache expiration time" strategy to accelerate both read and write data, and ensure regularly updated data, this model can meet most basic needs.

But if there are two problems arise at the same time, it might cause deadly harm to the application:

  • The current key is a hot key (for example, a popular entertainment news), a very large amount of concurrent
  • Rebuild the cache can not be completed in a short time, it can be a complex calculation, such as the SQL complex, the IO times, the plurality of dependence.

In the instant cache invalidation, a large number of threads to rebuild the cache, causing the back-end load increase, and may even allow the application to crash.

Popular point again: For some of the key set expiration time, assume that the key may be accessed concurrently at ultra-high point some time, is a very "hot" data. If just in times of high concurrency, the key date. . . . A large number of requests have hit the DB layer, resulting in a load DB is very large, and even downtime.

Cache breakdown solution (hot key reconstruction cache optimization)

To solve this problem is mainly to avoid a large number of threads simultaneously rebuild cache.

We can use a mutex to solve, this method only allows one thread cache reconstruction, reconstruction of other threads waiting threads executing the cache, you can retrieve data from the cache.

Pseudo code

String get(String key) {
	// 从Redis中获取数据
	String value = redis.get(key);
	// 如果value为空, 则开始重构缓存
	if (value == null) {
		// 只允许一个线程重建缓存, 使用nx, 并设置过期时间ex
		String mutexKey = "mutext:key:" + key;
		if (redis.set(mutexKey, "1", "ex 180", "nx")) {
		// 从数据源获取数据
		value = db.get(key);
		// 回写Redis, 并设置过期时间
		redis.setex(key, timeout, value);13 // 删除key_mutex
		redis.delete(mutexKey);
		}// 其他线程休息50毫秒后重试
		else {
			Thread.sleep(50);
			get(key);
		}
	}
	return value;
}
Published 825 original articles · won praise 2060 · Views 4.2 million +

Guess you like

Origin blog.csdn.net/yangshangwei/article/details/104933980