Home  >  Article  >  Java  >  How to use cache in SpringBoot project

How to use cache in SpringBoot project

PHPz
PHPzforward
2023-05-16 14:34:13945browse

Preface

Caching can effectively improve the performance and stability of the system by storing frequently accessed data in memory, reducing the pressure on underlying data sources such as databases. I think everyone has used it more or less in their projects, and our project is no exception. However, when I was reviewing the company's code recently, the writing was very stupid and low. The rough writing is as follows:

public User getById(String id) {
	User user = cache.getUser();
    if(user != null) {
        return user;
    }
    // 从数据库获取
    user = loadFromDB(id);
    cahce.put(id, user);
	return user;
}

In fact, Spring Boot provides a powerful caching abstraction that makes it easy to add caching to your application. This article will talk about how to use the different cache annotations provided by Spring to implement the best practices for caching.

Enable caching @EnableCaching

Now most projects are SpringBoot projects, we can add the annotation @EnableCaching to the startup class to enable the caching function.

@SpringBootApplication
@EnableCaching
public class SpringCacheApp {

    public static void main(String[] args) {
        SpringApplication.run(Cache.class, args);
    }
}

Since you want to be able to use cache, you need to have a cache manager Bean. By default, @EnableCaching will register a ConcurrentMapCacheManager Bean, no separate bean declaration. ConcurrentMapCacheManager stores the value in an instance of ConcurrentHashMap, which is the simplest thread-safe implementation of the caching mechanism.

Custom cache manager

The default cache manager cannot meet the needs because it is stored in the jvm memory, so how to store it in redis? At this time you need to add a custom cache manager.

1. Add dependencies

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2. Configure the Redis cache manager

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory();
    }

    @Bean
    public CacheManager cacheManager() {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .disableCachingNullValues()
            .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory())
            .cacheDefaults(redisCacheConfiguration)
            .build();

        return redisCacheManager;
    }
}

Now that we have the cache manager, how do we operate the cache at the business level?

We can use the @Cacheable, @CachePut or @CacheEvict annotation to operate the cache.

@Cacheable

This annotation can cache the results of method execution. When the method is called again within the cache time limit, the method itself will not be called, but the results will be obtained directly from the cache and returned to caller.

How to use cache in SpringBoot project

Example 1: Caching the results of database queries.

@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    @Cacheable(value = "myCache", key = "#id")
    public MyEntity getEntityById(Long id) {
        return repository.findById(id).orElse(null);
    }
}

In this example, the @Cacheable annotation is used to cache the results of the getEntityById() method based on its ID from the database Retrieve the MyEntity object from .

But what if we update the data? Old data still in cache?

@CachePut

Then @CachePut came out. The difference from the @Cacheable annotation is to use the @CachePut annotation annotation. The method will not check whether there are previously executed results in the cache before execution. Instead, the method will be executed every time and the execution results will be written to the specified cache in the form of key-value pairs. @CachePut Annotations are generally used to update cache data, which is equivalent to the cache using the double-write mode in the write mode.

@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    @CachePut(value = "myCache", key = "#entity.id")
    public void saveEntity(MyEntity entity) {
        repository.save(entity);
    }
}

@CacheEvict

The method marked @CacheEvict will remove the stored data from the cache when it is called. @CacheEvict Annotations are generally used to delete cached data, which is equivalent to the cache using the failure mode in write mode.

How to use cache in SpringBoot project

@Service
public class MyService {

    @Autowired
    private MyRepository repository;

     @CacheEvict(value = "myCache", key = "#id")
    public void deleteEntityById(Long id) {
        repository.deleteById(id);
    }
}

@Caching

@Caching Annotations are used to specify multiple Spring Cache related things on a method or class at the same time annotation.

How to use cache in SpringBoot project

Example 1: The evict attribute specified in the @Caching annotation becomes invalid when the method saveEntity is called Two caches.

@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    @Cacheable(value = "myCache", key = "#id")
    public MyEntity getEntityById(Long id) {
        return repository.findById(id).orElse(null);
    }

    @Caching(evict = {
        @CacheEvict(value = "myCache", key = "#entity.id"),
        @CacheEvict(value = "otherCache", key = "#entity.id")
    })
    public void saveEntity(MyEntity entity) {
        repository.save(entity);
    }

}

Example 2: When calling the getEntityById method, Spring will first check whether the result has been cached in the myCache cache. If so, Spring will return the cached result instead of executing the method. If the result is not already cached, Spring will execute the method and cache the result in the myCache cache. After the method is executed, Spring will remove the cached result from the otherCache cache based on the @CacheEvict annotation.

@Service
public class MyService {

    @Caching(
        cacheable = {
            @Cacheable(value = "myCache", key = "#id")
        },
        evict = {
            @CacheEvict(value = "otherCache", key = "#id")
        }
    )
    public MyEntity getEntityById(Long id) {
        return repository.findById(id).orElse(null);
    }

}

Example 3: When calling the saveData method, Spring will first remove data from the otherCache cache based on the @CacheEvict annotation. Spring will then execute the method and save the results to a database or external API. After the

method is executed, Spring will add the results to the myCache, myOtherCache and myThirdCache caches based on the @CachePut annotation. middle. Spring will also check whether the result has been cached in the myFourthCache and myFifthCache caches based on the @Cacheable annotation. If the result is not already cached, Spring will cache the result in the appropriate cache. If the result is already cached, Spring will return the cached result instead of executing the method again.

@Service
public class MyService {

    @Caching(
        put = {
            @CachePut(value = "myCache", key = "#result.id"),
            @CachePut(value = "myOtherCache", key = "#result.id"),
            @CachePut(value = "myThirdCache", key = "#result.name")
        },
        evict = {
            @CacheEvict(value = "otherCache", key = "#id")
        },
        cacheable = {
            @Cacheable(value = "myFourthCache", key = "#id"),
            @Cacheable(value = "myFifthCache", key = "#result.id")
        }
    )
    public MyEntity saveData(Long id, String name) {
        // Code to save data to a database or external API
        MyEntity entity = new MyEntity(id, name);
        return entity;
    }

}

@CacheConfig

通过@CacheConfig 注解,我们可以将一些缓存配置简化到类级别的一个地方,这样我们就不必多次声明相关值:

@CacheConfig(cacheNames={"myCache"})
@Service
public class MyService {

    @Autowired
    private MyRepository repository;

    @Cacheable(key = "#id")
    public MyEntity getEntityById(Long id) {
        return repository.findById(id).orElse(null);
    }

    @CachePut(key = "#entity.id")
    public void saveEntity(MyEntity entity) {
        repository.save(entity);
    }

    @CacheEvict(key = "#id")
    public void deleteEntityById(Long id) {
        repository.deleteById(id);
    }
}

Condition & Unless

  • condition作用:指定缓存的条件(满足什么条件才缓存),可用 SpEL 表达式(如 #id>0,表示当入参 id 大于 0 时才缓存)

  • unless作用 : 否定缓存,即满足 unless 指定的条件时,方法的结果不进行缓存,使用 unless 时可以在调用的方法获取到结果之后再进行判断(如 #result == null,表示如果结果为 null 时不缓存)

//when id >10, the @CachePut works. 
@CachePut(key = "#entity.id", condition="#entity.id > 10")
public void saveEntity(MyEntity entity) {
	repository.save(entity);
}


//when result != null, the @CachePut works.
@CachePut(key = "#id", condition="#result == null")
public void saveEntity1(MyEntity entity) {
	repository.save(entity);
}

清理全部缓存

通过allEntriesbeforeInvocation属性可以来清除全部缓存数据,不过allEntries是方法调用后清理,beforeInvocation是方法调用前清理。

//方法调用完成之后,清理所有缓存
@CacheEvict(value="myCache",allEntries=true)
public void delectAll() {
    repository.deleteAll();
}

//方法调用之前,清除所有缓存
@CacheEvict(value="myCache",beforeInvocation=true)
public void delectAll() {
    repository.deleteAll();
}

SpEL表达式

Spring Cache注解中频繁用到SpEL表达式,那么具体如何使用呢?

SpEL 表达式的语法

How to use cache in SpringBoot project

Spring Cache可用的变量

How to use cache in SpringBoot project

最佳实践

通过Spring缓存注解可以快速优雅地在我们项目中实现缓存的操作,但是在双写模式或者失效模式下,可能会出现缓存数据一致性问题(读取到脏数据),Spring Cache 暂时没办法解决。最后我们再总结下Spring Cache使用的一些最佳实践。

  • 只缓存经常读取的数据:缓存可以显着提高性能,但只缓存经常访问的数据很重要。很少或从不访问的缓存数据会占用宝贵的内存资源,从而导致性能问题。

  • 根据应用程序的特定需求选择合适的缓存提供程序和策略。SpringBoot 支持多种缓存提供程序,包括 EhcacheHazelcast 和 Redis

  • 使用缓存时请注意潜在的线程安全问题。对缓存的并发访问可能会导致数据不一致或不正确,因此选择线程安全的缓存提供程序并在必要时使用适当的同步机制非常重要。

  • 避免过度缓存。缓存对于提高性能很有用,但过多的缓存实际上会消耗宝贵的内存资源,从而损害性能。在缓存频繁使用的数据和允许垃圾收集不常用的数据之间取得平衡很重要。

  • 使用适当的缓存逐出策略。使用缓存时,重要的是定义适当的缓存逐出策略以确保在必要时从缓存中删除旧的或陈旧的数据。

  • 使用适当的缓存键设计。缓存键对于每个数据项都应该是唯一的,并且应该考虑可能影响缓存数据的任何相关参数,例如用户 ID、时间或位置。

  • 常规数据(读多写少、即时性与一致性要求不高的数据)完全可以使用 Spring Cache,至于写模式下缓存数据一致性问题的解决,只要缓存数据有设置过期时间就足够了。

  • 特殊数据(读多写多、即时性与一致性要求非常高的数据),不能使用 Spring Cache,建议考虑特殊的设计(例如使用 Cancal 中间件等)。

The above is the detailed content of How to use cache in SpringBoot project. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete