Springboot2.x basic tutorial: SpringCache cache abstraction detailed explanation and Ehcache, Redis cache configuration actual combat

In the history of computer development, a computer only needs external memory to run, but in practice, the speed of reading data from the disk often cannot keep up with the computing speed of the CPU, so the introduced memory is used as a buffer area between the CPU and the external memory. .
During the project development process, the query speed of database data is far inferior to the access speed of data in memory, so we usually use caching to improve the access speed of hot data. Caching can be described as the greatest invention in computer science.

Basic knowledge of caching

Cache hit rate

命中率=从缓存中读取的速度/总读取次数(从缓存读取次数+从慢速设备读取次数)
Miss率=没有从缓存中读取的速度/总读取次数(从缓存读取次数+从慢速设备读取次数)

This is a very important performance indicator for a cache. The higher the hit rate, the greater the effect of the cache.

Caching strategy

Removal strategy, that is, how to remove data from the cache if the cache is full

  1. FIFO (First in First Out): First in first out algorithm, that is, the data that is first put into the buffer is first removed
  2. LRU (Least Recently used): The data that has not been used for a long time will be removed first and limitedly.
  3. LFU (Least Frequently used): The least used data is limited and removed

Cache setting parameters

  1. TTL (Time To live): Lifetime, that is, a period of time from the point in time when it is created in the cache until it expires (no matter whether there is access during this period of time, it will expire)
  2. TTI (Time To Idle): Idle period, that is, how long a piece of data has not been accessed will be removed from the cache time.
  3. The most cached element

Spring annotation cache

After Spring 3.1, annotation caching technology was introduced, which is essentially not a specific caching implementation scheme, but an abstraction of the use of caching. By adding a small amount of custom annotations to the existing code, the use of caching can be achieved. The effect of the object and the returned object of the cache method. Spring's caching technology is quite flexible. It can not only use SpEL (Spring Expression Language) to define cache keys and various conditions, but also provides out-of-the-box cache temporary storage solutions, and supports integration with mainstream professional caches. Its characteristics are summarized as follows:

  1. A small amount of configuration annotations can make existing code support caching;
  2. Support out-of-the-box use, without installing and deploying additional third-party components to use the cache;
  3. Support Spring Express Language (SpEL), can use any attribute or method of the object to define the cache key and usage rule conditions;
  4. Support custom key and custom cache manager, with considerable flexibility and scalability.

Cache related solution

annotation Description Configuration parameter
@Cacheable Cache the results according to the request parameters of the method value: the name of the cache; key: the cached key, which can be empty or written according to SpEL expressions. If not specified, all parameters of the method will be combined by default; condition: the condition of the cache, which can be empty, use SpEL Write, return true or false, only if true is cached
@CachePut The results are cached according to the request parameters of the method. Unlike @Cacheable, it triggers the call of the real method every time value: the name of the cache; key: the cached key, which can be empty or written according to SpEL expressions. If not specified, all parameters of the method will be combined by default; condition: the condition of the cache, which can be empty, use SpEL Write, return true or false, only if true is cached
@CacheEvict Enough to clear the cache according to certain conditions value: the name of the cache; key: the cached key, which can be empty or written according to SpEL expressions. If not specified, all parameters of the method will be combined by default; condition: the cache condition, which can be empty, use SpEL Write, return true or false, only if true is cached; allEntries: whether to clear all cached content, the default is false, if specified as true, all caches will be cleared immediately after the method is called; beforeInvocation: whether to clear all caches before the method is executed , The default is false, if you specify true, the cache will be cleared before the method is executed. By default, if the method execution throws an exception, the cache will not be cleared

Integrated Ehcache cache

EhCache is a relatively mature Java caching framework, which was first developed from hibernate. It is an in-process caching system. It provides a variety of flexible cache management solutions such as memory, disk file storage, and distributed storage methods, which are fast and simple. .
Springboot supports the use of ehcache very much, so you only need to do some configuration in Springboot to use it, and it is easy to use.

Introduce dependencies

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

Configure Ehcache cache configuration

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!--磁盘路径,内存满了存放位置-->
    <diskStore path="caches"/>
    <cache name="users"
 	<!-- 最多存储的元素-->
	maxElementsInMemory="10000"
	<!--缓存中对象是否永久有效-->
    eternal="false"
	<!--缓存数据在失效前的允许闲置时间(单位:秒),仅当eternal=false时使用,默认值是0表示可闲置时间无穷大,若超过这个时间没有访问此Cache中的某个元素,那么此元素将被从Cache中清除-->
    timeToIdleSeconds="120"
	<!--缓存数据的总的存活时间(单位:秒),仅当eternal=false时使用,从创建开始计时,失效结束。-->
    timeToLiveSeconds="120"
	<!--磁盘缓存中最多可以存放的元素数量,0表示无穷大-->
    maxElementsOnDisk="10000000"
    diskExpiryThreadIntervalSeconds="120"
	<!--缓存策略-->
    memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </cache>
</ehcache>

applicatin.yml configures the ehcache address and adds the @EnableCaching annotation to the springboot startup class

spring:
  cache:
    ehcache:
      config: classpath:ehcache.xml
    type: ehcache

Configure Redis cache configuration

Introduce dependencies

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.2</version>
</dependency>

Redis configuration

@Configuration
@EnableCaching//开启缓存
public class RedisConfig extends CachingConfigurerSupport {

    private final static Logger log= LoggerFactory.getLogger(RedisConfig.class);

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

        return new RedisCacheManager(
                RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                this.getRedisCacheConfigurationWithTtl(60*60*24), // 默认策略,未配置的 key 会使用这个
                this.getRedisCacheConfigurationMap() // 指定 key 策略
        );
    }

    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        //可以进行过期时间配置
        redisCacheConfigurationMap.put("24h", this.getRedisCacheConfigurationWithTtl(60*60*24));
        redisCacheConfigurationMap.put("30d", this.getRedisCacheConfigurationWithTtl(60*60*24*30));
        return redisCacheConfigurationMap;
    }

    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();

        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext
                        .SerializationPair
                        .fromSerializer(jackson2JsonRedisSerializer)
        ).entryTtl(Duration.ofSeconds(seconds));

        //自定义前缀 默认为中间两个:
        redisCacheConfiguration = redisCacheConfiguration.computePrefixWith(myKeyPrefix());

        return redisCacheConfiguration;
    }

    /**
     * 缓存前缀(追加一个冒号 : )
     * @return
     */
    private CacheKeyPrefix myKeyPrefix(){
        return (name) -> {
            return name +":";
        };
    }

    /**
     * 生成Key规则
     * @return
     */
    @Bean
    public KeyGenerator wiselyKeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append("." + method.getName());
                if(params==null||params.length==0||params[0]==null){
                    return null;
                }
                String join = String.join("&", Arrays.stream(params).map(Object::toString).collect(Collectors.toList()));
                String format = String.format("%s{%s}", sb.toString(), join);
                //log.info("缓存key:" + format);
                return format;
            }
        };
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }


    /**
     * 缓存异常处理
     * @return
     */
    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
                log.info("redis缓存获取异常:"+ key);
            }

            @Override
            public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
                log.info("redis缓存添加异常:"+ key);
            }

            @Override
            public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
                log.info("redis缓存删除异常:"+ key);
            }

            @Override
            public void handleCacheClearError(RuntimeException e, Cache cache) {
                log.info("redis缓存清理异常");
            }
        };
        return cacheErrorHandler;
    }
}

applicatin.yml configures Redis connection information

spring:
  redis:
    timeout: 5000ms
    lettuce:
      pool:
        max-active: 5
        max-wait: -1
        max-idle: 10
  cache:
    type: redis

Examples of using cache annotations

Use cache for user query, add, delete, and update cache

public interface UserService {
    @Cacheable(value = "users",key = "#userDO.id")
    List<UserDO> queryUsers(UserDO userDO);
    @CachePut(value = "users",key ="#userDO.id" )
    void saveUser(UserDO userDO);
    @CacheEvict(value = "users",key = "#userDO.id")
    void removeUser(UserDO userDO);
}

A thousand miles begins with a single step. This is the fifteenth article of the SpringBoot tutorial series. All project source codes can be downloaded on my GitHub .

Guess you like

Origin blog.csdn.net/github_35592621/article/details/108301224