Local cache and distributed cache-manually implement a cache system

What is the cache and what does it do?

Cache refers to storing the commonly used data objects in the program or system in a specific medium such as memory to avoid the performance loss caused by re-creating or organizing data every time the program is called, thereby improving the system The overall operating speed.

With the current system architecture, the user's request generally passes through the cache system first. If there is no relevant data in the cache, the corresponding data will be queried in other systems and stored in the cache, and finally returned to the caller.

Local cache

The program-level cache component is characterized by the fact that the local cache and the application will run in the same process. The operation of the local cache is extremely fast, and there will be no network delay and overhead in the same process.

Local cache is suitable for single-node non-cluster application scenarios. Its advantage is that it is fast, but its disadvantage is that multiple programs cannot share the cache . For example, the distributed user session information is saved. Since the server accessed by each user may be different each time, if it cannot be shared Caching, then it means that each request operation may be blocked by the system, because the session information is only stored on a certain server. When the request is not forwarded to this server that stores the user information, it will be regarded as non-compliant. Login violation operation.

In addition, the inability to share the cache may cause a waste of system resources. This is because each system maintains its own cache separately, and the same cache may be stored separately by multiple systems, thus wasting system resource.

Can use EhCache and Google's Guava to achieve

Analysis of the use and characteristics of EhCache and Guava

EhCache supports memory cache and disk cache, and supports multiple elimination algorithms such as LRU (Least Recently Used), LFU (Least Frequently Used) and FIFO (First In First Out) , And supports a distributed cache system.

EhCache was originally an independent local cache framework component. In the later development (from version 1.2), it supports distributed cache. Distributed cache mainly supports RMI, JGroups, EhCache Server and other methods.

The LRU algorithm has a shortcoming. For example, if a key value that has not been used for a long time has been accessed once recently, it will not be eliminated even if it is the least used cache. The LFU algorithm solves the problem of being accessed once , The data will not be eliminated. It eliminates the data based on the total number of accesses. The core idea is "If the data has been accessed multiple times in the past, then it will be accessed more often in the future." Therefore, LFU can be understood as a more reasonable elimination algorithm than LRU.

Configure in the maven project

<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->

<dependency>

    <groupId>org.ehcache</groupId>

    <artifactId>ehcache</artifactId>

    <version>3.8.1</version>

</dependency>

EhCache use

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;

public class EhcacheLearn {
    
    
    public static void main(String[] args) {
    
    
        //创建缓存管理器
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
        //初始化 Ehcache
        cacheManager.init();
        //创建缓存(存储器)
        Cache<String, String> tangTangIsLearning = cacheManager.createCache("TangTangIsLearning",
                CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(10)));//设置缓存的最大容量
        //设置缓存
        tangTangIsLearning.put("key","value");
        //读取缓存
        String value = tangTangIsLearning.get("key");
        System.out.println(value);
        //关闭缓存
        cacheManager.close();


    }
}

CacheManager: It is a cache manager, which can be created by singleton or multiple instances, and it is also the entry class of Ehcache;

Cache: Each CacheManager can manage multiple Caches, and each Cache can store multiple elements in a hash mode.

Their relationship is shown in the following figure:

Insert picture description here

The feature of EhCache is that it is relatively simple to use, and its jar package is not small, and it can be used normally after simple configuration. EhCache is more flexible to use. It supports the configuration of multiple caching strategies. It supports both memory and disk caching. After EhCache 1.2, it also supports distributed caching.

Guava Cache is a sub-function of Google's open source Guava. It is a memory-based local cache implementation solution that provides a thread-safe cache operation mechanism.

The architectural design of Guava Cache is inspired by ConcurrentHashMap, which uses multiple segment-based fine-grained locks to ensure thread safety while supporting high-concurrency usage scenarios. Guava Cache operates on key-value pairs in a similar way to Map collections, but with additional processing logic such as expiration and elimination.

Distributed cache

Distributed cache refers to a caching mechanism that separates applications and cache components, so that multiple application systems can share a set of cached data. It is characterized by shared cache services and cluster deployment, providing high availability for the cache system Operating environment, and program operating mechanism for cache sharing.

Use Redis or Memcached. Since Redis itself is an independent caching system, it can be used as a third party to provide shared data caching, and Redis's distribution supports the master-slave, sentinel and cluster modes, so it can support distributed caching. In the case of Memcached It's similar.

Manually implement a caching system

There are currently three common expiration strategies

Timed delete

It means to create a timed event when setting the expiration time of the key value. When the expiration time is reached, the event handler will execute the operation of deleting the expired key. Its advantage is that it can release the memory space in time, but the disadvantage is that multiple delayed execution events need to be opened to process the cleanup task, which will cause a large number of task events to accumulate and occupy a lot of system resources.

Lazy deletion

The expired key will not be deleted actively, but it will be judged whether the value is expired every time it is requested. If it expires, the key value will be deleted, otherwise the normal value will be returned. Its advantage is that it only takes up a small amount of system resources, but the disadvantage is that it is not cleared in time and will cause a certain amount of space waste.

Delete regularly

It means to check the database at regular intervals and randomly delete some expired key values.

Redis uses two strategies: lazy deletion and periodic deletion.

First, you need to define an entity class that stores cache values. This class contains cache-related information, such as cache key and value, cache storage time, last use time, and hit times (reserved fields to support LFU cache Eliminate), and then use ConcurrentHashMap to save the cached key and value objects (the entity class of the cache value), and then add a tool class for caching operations to add and delete the cache, and finally when the cache is started, start a thread for To detect and delete the expired cache, the implementation code is as follows.

Entity class code:

import lombok.Getter;
import lombok.Setter;

import java.util.Comparator;

@Setter
@Getter
public class MyCacheEntity implements Comparable<MyCacheEntity> {
    
    
    private Object key;
    private Object value;
    private long lastTime;
    private long writeTime;
    private long  expireTime;
    private Integer hitCount;


    @Override
    public int compareTo(MyCacheEntity o) {
    
    
        return hitCount.compareTo(o.hitCount);
    }
}

Define the cache object

import java.util.concurrent.ConcurrentHashMap;

public class CacheGlobal {
    
    
    //全局缓存对象
    public static ConcurrentHashMap<String,MyCacheEntity> concurrentHashMap=new ConcurrentHashMap<>();
}

Cache tool class, with delete and update functions

import org.apache.commons.lang3.StringUtils;

public class CacheUtils {
    
    
    /**
     * 添加缓存
     * @param key
     * @param value
     * @param expire
     */
    public void put(String key, Object value, long expire) {
    
    
        // 非空判断,借助 commons-lang3
        if (StringUtils.isBlank(key)) return;
        // 当缓存存在时,更新缓存
        if (CacheGlobal.concurrentHashMap.containsKey(key)) {
    
    
            MyCacheEntity cache = CacheGlobal.concurrentHashMap.get(key);
            cache.setHitCount(cache.getHitCount() + 1);
            cache.setWriteTime(System.currentTimeMillis());
            cache.setLastTime(System.currentTimeMillis());
            cache.setExpireTime(expire);
            cache.setValue(value);
            return;
        }
        // 创建缓存
        MyCacheEntity cache = new MyCacheEntity();
        cache.setKey(key);
        cache.setValue(value);
        cache.setWriteTime(System.currentTimeMillis());
        cache.setLastTime(System.currentTimeMillis());
        cache.setHitCount(1);
        cache.setExpireTime(expire);
        CacheGlobal.concurrentHashMap.put(key, cache);
    }
    /**
     * 获取缓存
     * @param key
     * @return
     */
    public Object get(String key) {
    
    
        // 非空判断
        if (StringUtils.isBlank(key)) return null;
        // 字典中不存在
        if (CacheGlobal.concurrentHashMap.isEmpty()) return null;
        if (!CacheGlobal.concurrentHashMap.containsKey(key)) return null;
        MyCacheEntity cache = CacheGlobal.concurrentHashMap.get(key);
        if (cache == null) return null;
        // 惰性删除,判断缓存是否过期
        long timoutTime = System.currentTimeMillis() - cache.getWriteTime();
        // 缓存过期
        if (cache.getExpireTime() <= timoutTime) {
    
    
            // 清除过期缓存
            CacheGlobal.concurrentHashMap.remove(key);
            return null;
        }
        cache.setHitCount(cache.getHitCount() + 1);
        cache.setLastTime(System.currentTimeMillis());
        return cache.getValue();
    }
}

Check whether the cache expires at an interval of 10s, and the expiration is cleared

import java.util.concurrent.TimeUnit;

public class ExpireThread implements Runnable {
    
    


    @Override
    public void run() {
    
    
        int i=0;
        while (i<2)
            try {
    
    
                //每10s检查一次
                TimeUnit.SECONDS.sleep(10);
                expireCache();
            }catch (Exception e){
    
    
                e.printStackTrace();
            }finally {
    
    
                i++;
            }
    }

    /*缓存检测和清除方法*/
    private void expireCache()
    {
    
    
        System.out.println("检测缓存是否过期");
        for (String key : CacheGlobal.concurrentHashMap.keySet()) {
    
    
            MyCacheEntity cache = CacheGlobal.concurrentHashMap.get(key);
            //当前时间减去写入时间
            long timOutTime= System.currentTimeMillis() - cache.getWriteTime();
            if(cache.getExpireTime()>timOutTime)
            {
    
    
                //没有过期
                continue;
            }
            //清除过期缓存
            CacheGlobal.concurrentHashMap.remove(key);
        }

    }
}

Test class

import java.util.concurrent.TimeUnit;

public class MyCacheTest {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        CacheUtils cache=new CacheUtils();
        cache.put("name","TangTang",10);
        String name = (String) cache.get("name");
        System.out.println(name);
        String sex = (String) cache.get("Sex");
        System.out.println(sex);
        new ExpireThread().run();
        TimeUnit.SECONDS.sleep(13);
        String name2 = (String) cache.get("name");
        System.out.println(name2);
    }
}

Guess you like

Origin blog.csdn.net/tangshuai96/article/details/111396598
Recommended