Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

The cache

Caches are divided into local caches and distributed caches. To ensure thread safety, local caches are generally stored in memory in the way of ConcurrentMap, while common distributed caches include Redis and MongoDB.

Consistency: Local cache Since data is stored in memory and each instance has its own copy, there may be inconsistencies. Distributed caches can effectively avoid this overhead: local caches can consume JVM memory and affect GC and system performance; The overhead of distributed cache lies in network delay and object serialization, which mainly affects the call delay. Distributed cache applies to scenarios with high consistency requirements and a large amount of data (flexible capacity expansion). Local cache applies to data with a small or small amount of data, because a large number of changes require consideration of cache consistency between different instances, while a large number of data requires consideration of cache reclamation policies and ISSUES related to GC

Guava cache

Guava Cache is a memory caching module in Google Fuava that is used to Cache data into JVM memory.

Provide get, PUT encapsulation operations, can integrate data sources; Thread-safe cache, similar to ConcurrentMap, but with more element invalidation policies, while ConcurrentMap can only display removed elements. Guava Cache provides a number of basic Cache reclamation methods to monitor Cache load/hit situations In general, Guava Cache is suitable for the following situations:

Be willing to spend some memory to speed things up. Usage scenarios Sometimes the key is queried multiple times. The cache will not need to store more data than RAM capacity Details the concurrency level of the cache Guava provides an API to set the concurrency level so that the cache supports concurrent writes and reads. Similar to ConcurrentHashMap, Guava cache concurrency is achieved through split locks. In general, it is recommended that the concurrency level be set to the number of server CPU cores.

Cachebuilder.newbuilder () // sets the concurrencyLevel to number of CPU cores. The default is 4. ConcurrencyLevel (runtime.getruntime ().availableprocessors ()).build();Copy the code

The initial capacity setting for the cache

When we build the cache, we can set a reasonable initial capacity for the cache, which is very expensive because Guava’s cache uses a split lock mechanism. Therefore, a reasonable initial capacity can reduce the capacity expansion times of the cache container.

Cachebuilder.newbuilder () // set the initialCapacity to 100.initialcapacity (100).build();Copy the code

Setting the maximum storage

Guava Cache can specify the maximum number of records that the Cache can store when the Cache object is built. When the number of records in the Cache reaches the maximum, Guava calls the PUT method to add an object to the Cache. Guava deletes an object from the current Cache to make room for the new object.

Cachebuilder.newbuilder () // Set the maximum capacity to 1000.maximumSize (1000).build();Copy the code

Cache cleanup policy

Cleanup strategy based on lifetime

ExpireAfterWrite Expiration after write cache expireAfterAccess Expiration after read/write cache Lifetime Policies can be set individually or in combination

Capacity – based clearance policy

The cachebuilder.maximumsize (long) method sets the maximumSize of the Cache. When the number of caches reaches or approaches this maximum, the least recently used caches are flushed

Weight – based clearance strategy

Use CacheBuilder. Weigher (weigher) to assign a weighting function, and use CacheBuilder. MaximumWeight (long) specifies the maximum total weight.

If each cache occupies a different size of memory space, it can be considered that they have different “weights” (weights), which can be used to optimize the collection of objects when executing the purge strategy

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() .maximumWeight(100000) .weigher(new Weigher<Key, Graph>() { public int weigh(Key k, Graph g) { return g.vertices().size(); } }) .build( new CacheLoader<Key, Graph>() { public Graph load(Key key) { // no checked exception return createExpensiveGraph(key); }});Copy the code

Explicitly clear

Delete a single key: cache. invalidate(key) Delete a batch key: cache. invalidateAll(keys) Delete all Cache entries: Cachebuilder.invalidateall () allows the JVM to clean the Cache during GC by setting the weak-referenced key, weak-referenced value, or weak-referenced value. WeakKeys () : Use weak references to store keys. Cache items can be garbage collected when there are no other (strong or soft) references to the key. Cachebuilder.weakvalues () : Stores values using weak references. Cachebuilder.softvalues () : Stores the value with a soft reference. Soft references are recycled in the order of least recently used globally only in response to memory needs. Given the performance impact of using soft references, we generally recommend using the more predictive cache size limit garbage collection that relies only on the == identity, and caching that uses weak reference keys instead of equals(), the same object reference.

Cache

An explicit PUT operation is put into memory

private static Cache<Integer, Integer> numCache = CacheBuilder.newBuilder()
        .expireAfterWrite(5, TimeUnit.MINUTES)
        .build();

public static void main(String[] args) throws Exception {
    System.out.println(numCache.getIfPresent(1));
    Thread.sleep(1000);
    System.out.println(numCache.getIfPresent(1));
    Thread.sleep(1000);
    numCache.put(1, 5);
    System.out.println(numCache.getIfPresent(1));
    // console: null null 5
}
Copy the code

LoadingCache

Use custom ClassLoader to load data into memory. If the data is retrieved from LoadingCache, it is returned directly. If the data does not exist, load the data to the memory according to the load method of the ClassLoader and return the data

private static LoadingCache<Integer,Integer> numCache = CacheBuilder.newBuilder(). expireAfterWrite(5L, TimeUnit.MINUTES). maximumSize(5000L). build(new CacheLoader<Integer, Integer>() { @Override public Integer load(Integer key) throws Exception { System.out.println("no cache"); return key * 5; }}); public static void main(String[] args) throws Exception { System.out.println(numCache.get(1)); Thread.sleep(1000); System.out.println(numCache.get(1)); Thread.sleep(1000); numCache.put(1, 6); System.out.println(numCache.get(1)); // console: 5 5 6 } ```Copy the code