Millet mall microservice distributed advanced Chapter 10 - cache SpringCache

SpringCache

summary

Spring 3.1 introduces the exciting annotation based cache technology. In essence, it is not a specific cache implementation scheme (such as EHCache or OSCache), but an abstraction of cache use. By adding a small number of annotations it defines to the existing code, it can achieve the effect of caching the return object of the method.

Spring's caching technology also has considerable flexibility. It can not only use Spring Expression Language (SpEL) to define cached key s and various condition s, but also provide out of the box cache temporary storage solutions, and support integration with mainstream professional caches such as EHCache.

Its characteristics are summarized as follows:

  • Existing code can support caching through a small number of configuration annotation annotations
  • Support out of the box, that is, cache can be used without installing and deploying additional third-party components
  • Supports Spring Express Language, and can use any attribute or method of the object to define the cached key and condition
  • Supports AspectJ and implements caching support for any method through it
  • It supports user-defined key s and cache managers, and has considerable flexibility and scalability

Explain Spring Cache in one sentence: use the idea of AOP to liberate cache management through annotation.

Basic concepts

SpringCache

Simple example

Create a Spring project

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo.cache</groupId>
    <artifactId>cache</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cache</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Create a Book Model

public class Book {

  private String isbn;
  private String title;

  public Book(String isbn, String title) {
    this.isbn = isbn;
    this.title = title;
  }

  public String getIsbn() {
    return isbn;
  }

  public void setIsbn(String isbn) {
    this.isbn = isbn;
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  @Override
  public String toString() {
    return "Book{" + "isbn='" + isbn + '\'' + ", title='" + title + '\'' + '}';
  }

}

Create Library

public interface BookRepository {

  Book getByIsbn(String isbn);

}

Simulated latency repository

@Component
public class SimpleBookRepository implements BookRepository {

  @Override
  public Book getByIsbn(String isbn) {
    simulateSlowService();
    return new Book(isbn, "Some book");
  }

  //Simulatesllowservice intentionally inserts a three second delay in each getByIsbn. Later, the example will be accelerated through caching.
  private void simulateSlowService() {
    try {
      long time = 3000L;
      Thread.sleep(time);
    } catch (InterruptedException e) {
      throw new IllegalStateException(e);
    }
  }

}

Using the library
CommandLineRunner injects BookRepository and calls it multiple times with different parameters

@Component
public class AppRunner implements CommandLineRunner {

  private static final Logger logger = LoggerFactory.getLogger(AppRunner.class);

  private final BookRepository bookRepository;

  public AppRunner(BookRepository bookRepository) {
    this.bookRepository = bookRepository;
  }

  @Override
  public void run(String... args) throws Exception {
    logger.info(".... Fetching books");
    logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"));
    logger.info("isbn-4567 -->" + bookRepository.getByIsbn("isbn-4567"));
    logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"));
    logger.info("isbn-4567 -->" + bookRepository.getByIsbn("isbn-4567"));
    logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"));
    logger.info("isbn-1234 -->" + bookRepository.getByIsbn("isbn-1234"));
  }

}

If you try to run the application at this time, it will be very slow even if you retrieve identical books many times. The following example output shows the three second delay created by our (intentional delay) code:

Next, add cache @Cacheable to the method of getting the book

@Component
public class SimpleBookRepository implements BookRepository {

  @Override
  @Cacheable("books")
  public Book getByIsbn(String isbn) {
    simulateSlowService();
    return new Book(isbn, "Some book");
  }

  // Simulatesllowservice intentionally inserts a three second delay in each getByIsbn. Later, you will speed up the sample through caching.
  private void simulateSlowService() {
    try {
      long time = 3000L;
      Thread.sleep(time);
    } catch (InterruptedException e) {
      throw new IllegalStateException(e);
    }
  }

}

Step 2 annotate the startup class @EnableCaching enable caching

@SpringBootApplication
//@After the EnableCaching annotation is triggered, the processor checks the existence of the public method cache annotation of each Spring bean. If such annotations are found, a proxy is automatically created to intercept method calls and handle cached behavior accordingly.
@EnableCaching
public class CacheApplication {

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

}

It still takes three seconds to start the service and find the first time to retrieve the book. However, the second and subsequent times of the same book are much faster, indicating that the cache is doing its job.

Congratulations! You have just enabled caching on Spring managed bean s.

annotation

@Role of Cacheable

  • Mainly for method configuration, it can cache the results according to the request parameters of the method

@Main parameters of Cacheable

  • value The name of the cache, defined in the spring configuration file, must specify at least one For example:
    @Cacheable(value= "mycache") or @Cacheable(value={"cache1", "cache2"}

  • Key The cached key can be empty. If it is specified to be written according to the SpEL expression, if it is not specified, it will be combined according to all the parameters of the method by default For example: @Cacheable(value= "testcache", key= "\userName")

  • Condition The cache condition can be null. It is written in spiel and returns true or false. Only when it is true can it be cached For example: @Cacheable(value= "testcache", condition= "\username.length() >2")

@Role of CachePut

  • Mainly for method configuration, it can cache the results according to the request parameters of the method. Unlike @Cacheable, it will trigger the call of the real method every time

@Main parameters of CachePut

  • value The name of the cache, defined in the spring configuration file, must specify at least one For example:
    @Cacheable(value= "mycache") or @Cacheable(value={"cache1", "cache2"}

  • Key The cached key can be empty. If it is specified to be written according to the SpEL expression, if it is not specified, it will be combined according to all the parameters of the method by default For example: @Cacheable(value= "testcache", key= "\userName")

  • Condition The cache condition can be null. It is written in spiel and returns true or false. Only when it is true can it be cached For example: @Cacheable(value= "testcache", condition= "\username.length() >2")

@Role of CachEvict

  • Mainly for method configuration, it can clear the cache according to certain conditions (delete the cache after modifying the data)

@Main parameters of CacheEvict

  • value The name of the cache, defined in the spring configuration file, must specify at least one For example:
    @CachEvict(value= "mycache") or @CachEvict(value={"cache1", "cache2"}

  • Key The cached key can be empty. If it is specified to be written according to the SpEL expression, if it is not specified, it will be combined according to all the parameters of the method by default For example: @CachEvict(value= "testcache", key= "\userName")

  • Condition The cache condition can be empty. It is written in spiel and returns true or false. Only when it is true will the cache be cleared For example: @CachEvict(value= "testcache", condition= "\username.length() >2")

  • allEntries Whether to clear all cache contents. The default value is false. If true is specified, all caches will be cleared immediately after the method is called For example: @CachEvict(value= "testcache", allEntries=true)

  • beforeInvocation Whether to clear the method before execution. The default is false. If true is specified, the cache will be cleared before the method is executed. By default, if the method throws an exception, the cache will not be cleared

@Caching

  • @The Caching annotation allows us to specify multiple Spring Cache related annotations on a method or class at the same time. It has three attributes: Cacheable, put and evict, which are used to specify @Cacheable, @CachePut and @CacheEvict respectively.

Basic principles

Similar to the transaction management of spring, the key principle of spring cache is spring AOP. Through spring AOP, it can obtain the input parameter and return value of the method before and after the method call, and then realize the logic of cache. Let's take a look at the following figure:


The above figure shows that when the client "Calling code" calls the foo() method of a common Plain Object class, it directly acts on the pojo class's own object, and the client has a direct reference to the callee.

Spring cache makes use of the dynamic proxy technology of Spring AOP, that is, when the client tries to call the foo () method of pojo, it is not the reference of pojo itself, but a dynamically generated proxy class

As shown in the above figure, at this time, the actual client has a proxy reference. When calling the foo() method, the proxy foo() method will be called first. At this time, the proxy can control the actual POJO as a whole The input parameter and return value of foo() method, such as caching results, and skipping the actual foo() method, can be easily achieved.

Integrate SpringCache to simplify cache development

1) . import dependency

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

2) . write configuration

  • What is automatically configured

Cacheouroconfiguration will import RedisCacheConfiguration; RedisCacheManager is automatically configured

  • Configure to use redis as cache

spring.cache.type=redis

3) . annotate the startup class to open the cache

@EnableCaching

4) . annotate the method @Cacheable("categories")

    /**
     * Query all first level classifications
     *
     * @return
     */

    @Override
    @Cacheable("categorys")
    public List<CategoryEntity> getLevel1Categorys() {
        System.out.println("Query all first level classifications");
        List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));

        return categoryEntities;
    }

  • For each data to be cached, we specify the cache to be put into that name [cache partition (by service type)]
  • @Cacheable("categorys")
  • The result representing the current method needs to be cached. If there is one in the cache, the method does not need to be called
  • If it is not in the cache, the method will be called, and the result of the service method will be put into the cache

Default behavior

  • If there is in the cache, the method does not need to be called
  • The key is automatically generated by default. The name of the cache: SimpleKey[] (the self generated key value)
  • The cached value uses the jdk serialization mechanism by default to save the serialized data to redis
  • Default ttL time -1

5) , generated cache

custom

  • Specify the key used by the generated cache

@Cacheable(value = "categorys", key = "#root.method.name")

  • Specifies the lifetime of cached data
    Modify ttl in configuration file, unit: ms

    spring.cache.redis.time-to-live=3600000

  • Save data in json format

The cache manager needs to be modified. The principle of spring cache cache manager is as follows:
Cacheauthoconfiguration the cache configuration class will import RedisCacheConfiguration;
RedisCacheConfiguration automatically configures RedisdCacheManager;
RedisCacheConfiguration initializes all caches;
Each cache determines what configuration to use;
If redisCacheConfiguration is available, use the existing one; if not, use the default configuration;
To change the cache configuration, you only need to put a RedisCacheConfiguration in the container, which will be applied to all caches managed by the current RedisCacheManager;

Write MyCacheConfig injection container

@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching
@Configuration
public class MyCacheConfig {

//    @Autowired
//    CacheProperties properties;

    //1. The original configuration class bound to the configuration file looks like this
    //@ConfigurationProperties(prefix = "spring.cache")
    //public class CachePropertiesT
    //2. To make him effective
    //@EnableConfigurationProperties(CacheProperties.class)

    @Bean
    RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();

       config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
       config =  config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericFastJsonRedisSerializer()));

        //Validate the configuration in the configuration file
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }

        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }

        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }

        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }

        return config;

    }


}

application.properties

spring.cache.type=redis
spring.cache.redis.time-to-live=3600000
#If a prefix is specified, the prefix we specify will be used. If not, the cached name will be used as the prefix by default
spring.cache.redis.key-prefix=CACHE_
#Enable key prefix
spring.cache.redis.use-key-prefix=true
#Whether to cache null values. Prevent cache penetration
spring.cache.redis.cache-null-values=true

ok

summary

Insufficient Spirng cache
1) Read mode:

  • Cache penetration: query a null data. Solution: cache empty data: cache null values=true
  • Cache breakdown: a large number of concurrent queries come in and query a data that has just expired. Solution: lock, no lock by default, sync=true
  • Cache avalanche: a large number of Keys expire at the same time. Solution: add random time. Add the expiration time spring Cache Redis Time to live=3600000

2) Write mode: (cache is consistent with database)

  • 1: Write lock
  • 2: Introduce canal and feel the changes of mysql
  • 3: Read more and write more. Just go to the database and query

Regular data (spring cache can be used for data with more reads and less writes, which is instant and does not require high consistency)
Special data: special design

Tags: MySQL Java Spring Redis Spring Boot

Posted by BMurtagh on Mon, 30 May 2022 19:50:58 +0530