SpringBoot Cache

The main function of enterprise-level applications is information processing. When data needs to be read, if it is read directly from the database, it will put great pressure on the data layer. At the same time, it is limited by the access efficiency of the database, resulting in low overall system performance.

A temporary data storage mechanism is usually established between the application and the database. The data in this area is saved in the memory, and the read and write speed is fast, which can effectively solve the problem of low database access efficiency. This area where data is temporarily stored is the cache.

Cache is a temporary data storage medium between the permanent data storage medium and the application. The use of cache can effectively reduce the number of low-speed data reading processes (such as disk IO) and improve system performance. In addition, caching can not only be used to improve the data reading efficiency of permanent storage media, but also provide temporary data storage space.

SpringBoot built-in cache

Springboot technology provides a built-in caching solution, which can help developers quickly enable caching technology and use caching technology to perform fast data operations, such as reading cached data and writing data to the cache.

Introduce dependencies

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

Enable caching

Mark the annotation @EnableCaching above the boot class to configure the cache to be used in the springboot program. Example:

@SpringBootApplication
@EnableCaching
public class SpringbootMongodbApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootMongodbApplication.class, args);
    }
}

Business use

Enable caching

Use @Cacheable annotation

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    
    @Cacheable(value="cacheSpace",key="#id")
    public User getById(Integer id) {
        return userDao.selectById(id);
    }
}

Note: Use the annotation @Cacheable on the business method to declare that the return value of the current method is placed in the cache. The storage location of the cache must be specified, and the name corresponding to the return value of the current method is saved in the cache. In the above example, the value attribute describes the storage location of the cache, which can be understood as a storage space name. The key attribute describes the name of the data saved in the cache. Use #id to read the id value in the formal parameter as the cache name. Pay attention to the format of the key (key=”#id”). Currently, your own testing needs to be consistent with the parameters of the method.

After using the @Cacheable annotation, perform the current operation. If it is found that the corresponding name has no data in the cache, the data will be read normally and then placed in the cache; if the corresponding name has data in the cache, the execution of the current business method will be terminated and directly returned to the cache. data in.

Cache eviction

Using the @CacheEvict annotation, allEntries = true means to reclaim the cache of all key values in the current cache space. You can also specify key=”#id” to recall the cache of a specific key value.

@ApiOperation("Delete customer information configuration")
@RequestMapping(method = {RequestMethod.POST}, path = {"/deleteCustomerInfo"})
@CacheEvict(value = "cduCustomerNoListCache", allEntries = true)
public BaseResponseMessage<Boolean> deleteCustomerInfo(
    @ApiParam(name = "uuid", required = true, value = "uuid", example = "") @RequestParam @NotBlank String uuid
) {
    …
    //2Delete basic customer information
    Boolean success = customerInfoService.removeById(uuid);
    …
    return BaseResponseMessage.success(success);
}

There are deficiencies

1. As stated in the Spring documentation, Spring’s default caching system does not have TTL (Time to live).

If TTL configuration is required, you must use another cache provider such as Redis or Gemfire.

2. Restarting the service will also cause the cache to be cleared.

SpringBoot + Redis

Based on the above work, add the following configuration:

Add dependencies

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

Configuration yml

spring:
  cache:
    type: redis
#redis:
# time-to-live: 10s #Set cache existence time
# key-prefix: wxl_ #Set the prefix of key
#cache-null-values: true #Whether to cache null values
  redis:
    host: localhost
    port: 6379

Remark:

  1. The cache validity period is set through time-to-live.
  2. Cache penetration solution: configure spring.cache.redis.cache-null-values=true in the configuration file
  3. Cache breakdown solution: @Cacheable(value = {“category”},key = “#root.method.name”,sync = true) [sync = true lock]

Several situations in which it does not take effect

  1. Another method call of this class. The reason is that @Cacheable is implemented based on Spring AOP, and the inner class does not go through the AOP proxy, so it does not take effect. Simply mention the annotated method to another class to solve the problem.
  2. Does not support non-public methods, even protected methods, let alone private methods.
  3. The cached object must implement Serializable, that is
public class Role implements Serializable{
}

Multiple TTL settings

I have searched a lot of information on the Internet, but I have not yet found a way to set multiple TTLs through yml. The current summary is as follows:

1. Comment out part of the configuration in yml and customize the expiration time of the cache space

#Cache
  cache:
  type: redis
# redis: When customizing ChacheManager, the configuration here does not need to be configured, and it will not work if configured.
# use-key-prefix: true
# key-prefix: cdu_
# cache-null-values: true
# time-to-live: 60s
# cache-names: cache1, cache2
cache:
ttl: '{"cache1":1,"cache2":24}' #Customize the expiration time of cache space, unit hour

Note: After customizing ChacheManager, yml related configuration will not work.

2. Customize ChacheManager

/**
 * Solve the problem that cache (@Cacheable) caches data into redis and the value is garbled.
 *@authorXXM
 * @date 2023/8/26
 * @apiNote
 */
@Configuration
public class CacheRedisConfig {
    // ${cache} obtains the configuration information of the configuration file #{} is a spring expression and obtains the properties of the Bean object
    @Value("#{${cache.ttl}}")
    private Map<String, Long> ttlParams;
    
    @Bean
    @SuppressWarnings("all")
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
                .defaultCacheConfig()
                //Configure the prefix of the cache space name
                .prefixCacheNameWith("cdu_cache_")
                //Globally configure the cache expiration time (can not be configured)
                //.entryTtl(Duration.ofHours(1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer.UTF_8))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                ;
        //Specially specify the configuration expiration time of certain cache spaces
        Map<String, RedisCacheConfiguration> initialCacheConfigurationsMap = new HashMap<>();
        Set<Map.Entry<String, Long>> entries = ttlParams.entrySet();
        for (Map.Entry<String, Long> entry : entries) {
            //Specify the expiration time corresponding to a specific cache space
            initialCacheConfigurationsMap.put(entry.getKey(), redisCacheConfiguration.entryTtl(Duration.ofHours(entry.getValue())));
        }
        RedisCacheManager cacheManager = RedisCacheManager
                .builder(redisConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .withInitialCacheConfigurations(initialCacheConfigurationsMap) //Specific configurations of some cache spaces
                .build();
        return cacheManager;
    }
}