This is the 11th day of my participation in the Gwen Challenge.More article challenges

Preface:

Along with the development of the Internet, users and the amount of information is becoming more and more complex traffic is becoming more and more large at the same time, our application server resources is limited, the database can accept the file to read and write requests per second times is limited, as much as possible in order to be able to effectively use limited resources to provide greater throughput, an effective way is to introduce the cache, Cache is now an essential module in the system, and has become the high concurrency, a key component of high performance architectural break the standard process, some requests we can get the target data directly from the cache and returns, thus reducing the amount of calculation, reduce the number of database, speaking, reading and writing, effectively improve response speed, make limited resources more user services.

Cache:

We generally classify caches into the following two categories

Local cache

It refers to the cache component in the application which is the level 1 cache

Advantages:

  • Applications and cache reside in the same process and are stored in the memory of the server. Cache reads and writes are very fast and do not have too much network overhead. Local cache is suitable for scenarios where a single application does not need cluster support or nodes do not need to notify each other.

Disadvantages:

  • The cache is coupled with the application, so multiple applications cannot directly share the cache. Each application or cluster node needs to maintain its own cache, which is a waste of memory.
  • The read cache data is stored directly in the JVM, so you need to consider the size of the cache data and the garbage collection performance cost of the JVM
  • If a single service is deployed in a cluster, you should consider whether to synchronize data from the local cache in the cluster
Remote Cache

A cache component or service is separated from an application. The biggest advantage of a cache component or service is that it is an independent application and is isolated from a local application. Multiple applications can directly share the cache. For example, mongodb, memcached and Redis, which are common in our work, are also skills we must master.

At present, there are various types of cache components in the market, and each cache scheme has its own business scenarios. We need to accurately determine what type of cache to use and how to use this cache according to our own business scenarios and data types, so as to achieve the optimal purpose with the lowest cost and the fastest efficiency.

Guava Cache

When we need to use local cache in a project, we usually implement local cache based on ConcurrentHashMap or LinkedHashMap. However, the following problems are common when using local caching:

  1. Java memory is limited, so you need to limit the maximum size of the cache.
  2. How to clear “too old” caches.
  3. How to deal with concurrent read and write security.

Guava Cache is similar to ConcurrentMap in that it is thread-safe, but not exactly. The basic difference is that ConcurrentMap keeps all added elements until they are explicitly removed. Guava Cache, on the other hand, is usually set to auto-recycle elements with an expiration time to limit memory footprint. Although LoadingCache does not reclaim elements, it is useful in some scenarios because it automatically loads the cache.

Code examples:

import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** * @author taoze * @version 1.0 * @date 6/9/21 3:27pm */ @component public class LocalCacheUtil { private static LoadingCache<String, String> cache = CacheBuilder.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .refreshAfterWrite(5, TimeUnit.MINUTES) .build(new CacheLoader<String, String>() { @Override public String load(String key) { return safeGetTokenFromLocal(key); }}); private static String safeGetTokenFromLocal(String key) { return key+":"+UUID.randomUUID().toString().replaceAll("-", ""); } public static String getToken(String key){ String value = cache.getIfPresent(key); if(StringUtils.isBlank(value)){ value = safeGetTokenFromLocal(key); } return value; } public static void main(String[] args) throws ExecutionException { String test = getToken("test"); System.out.println(test); }}Copy the code

As a simple example, let’s look at three time-based ways CacheBuilder cleans or flushes cached data:

ExpireAfterAccess: A cache item is reclaimed when it has not been read or written within a specified period of time. ExpireAfterWrite: A cache item is reclaimed if it has not been updated within a specified period of time. RefreshAfterWrite: How long has the cache entry been refreshed since the last update operation?

For timeliness, we can use expireAfterWrite to invalidate the cache at a specified time after each update and then reload the cache. Guava Cache is strictly limited to one load operation, which is a good way to prevent an avalanche of requests from penetrating the back end when the cache fails.

RefreshAfterWrite has a higher performance than expireAfterWrite because it is restricted to only one reload operation during refresh and all other queries return the old value first. But it also has a disadvantage, because it does not strictly guarantee that all queries will get the new value after the specified time

RefreshAfterWrite and expireAfterWrite have their own advantages and disadvantages and different usage scenarios. So we use both methods, controlling the cache to refresh every 5 minutes, and invalidating the cache if it is not accessed for more than 10 minutes. The next time the cache is accessed, it does not get the old value, but must wait for the new value to load.

Common method API:

V getIfPresent(Object key) Retrieves the value of the key in the cache. If the cache fails, null is returned. V get(K key) Throws ExecutionException Obtains the value corresponding to the key. If it does not exist in the cache, call the load method of LocalCache to load the key from the data source and cache the key. Void put(K key, V value) If the cache has a value, overwrite it; otherwise, add void putAll(Map m). Void invalidate(Object key); Void invalidateAll(); Clear all caches, quite far from map's clear operation. long size(); Gets the approximate number of elements in the cache. Why roughly? When an element fails, size is not updated in real time, so size may contain the failed element. CacheStats stats(); Cache status data, including number of hits, number of loading successes/failures, total loading time, number of deletions, etc. The asMap() method gets the ConcurrentMap snapshot of the cached data: cleanUp(); refresh(Key) Update cache ImmutableMap getAllPresent(Iterable keys) to retrieve cached values for multiple keys at onceCopy the code

Core classes:

CacheBuilder: class, CacheBuilder. Entry to build the cache, specify the cache configuration parameters, and initialize the local cache. The CacheBuilder passes all of the previously set parameters to LocalCache in the build method, without actually participating in any calculations of its own. This method of initializing parameters is worthy of reference, and the code is concise and easy to read. CacheLoader: Abstract class. Used to load data from data sources and define operations such as load, reload, and loadAll. Cache: Interface that defines operations such as GET, PUT, and invalidate. This interface only adds, deletes, and modifies the Cache, but does not load data. AbstractCache: Abstract class that implements the Cache interface. Among them, batch operations are cyclic execution of a single behavior, and the single behavior is not defined. LoadingCache: interface inherited from Cache. Define operations like GET, getUnchecked, and getAll, which load data from the data source. AbstractLoadingCache: Abstract class derived from AbstractCache that implements the LoadingCache interface. LocalCache: class. The core class of the entire Guava cache contains the data structure of the Guava cache and the basic operation methods of the cache. LocalManualCache: Internal static class of LocalCache that implements the Cache interface. The internal add, delete, or change cache operations all call the corresponding methods of the member variable localCache (type localCache). LocalLoadingCache: Internal static class of LocalCache that inherits from the LocalManualCache class and implements the LoadingCache interface. All of its operations are also methods that call the member variable localCache (of type localCache). CacheStats: Cache load/hit statistics.Copy the code

Ok! Today’s sharing ends here, we can choose the type of cache according to their own business situation, I hope it can be helpful to you, there are wrong places I hope we can put forward, grow together;

Neat makes for great code, and there’s only so much detail