[Redis] Redis and SSM integration&Redis annotation caching&Redis solves caching problems

1. Integration of Redis and ssm

1.1 pom.xml configuration

Configure related redis files in pom.xml

redis file:

<redis.version>2.9.0</redis.version>
<redis.spring.version>1.7.1.RELEASE</redis.spring.version>
 
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${redis.version}</version>
</dependency>

The entire pom.xml file:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>ssm2</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>ssm2 Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.plugin.version>3.7.0</maven.compiler.plugin.version>

    <!--Add jar package dependencies-->
    <!--1.spring 5.0.2.RELEASE related-->
    <spring.version>5.0.2.RELEASE</spring.version>
    <!--2.mybatis related-->
    <mybatis.version>3.4.5</mybatis.version>
    <!--mysql-->
    <mysql.version>5.1.44</mysql.version>
    <!--pagehelper paging jar dependency-->
    <pagehelper.version>5.1.2</pagehelper.version>
    <!--mybatis and spring integrated jar dependencies-->
    <mybatis.spring.version>1.3.1</mybatis.spring.version>
    <!--3.dbcp2 connection pool related druid-->
    <commons.dbcp2.version>2.1.1</commons.dbcp2.version>
    <commons.pool2.version>2.4.3</commons.pool2.version>
    <!--4.log log related-->
    <log4j2.version>2.9.1</log4j2.version>
    <!--5.Others-->
    <junit.version>4.12</junit.version>
    <servlet.version>4.0.0</servlet.version>
    <lombok.version>1.18.2</lombok.version>

    <ehcache.version>2.10.0</ehcache.version>
    <slf4j-api.version>1.7.7</slf4j-api.version>

    <redis.version>2.9.0</redis.version>
    <redis.spring.version>1.7.1.RELEASE</redis.spring.version>
  </properties>

  <dependencies>
    <!--1.spring related-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!--2.mybatis related-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis.version}</version>
    </dependency>
    <!--mysql-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>${mysql.version}</version>
    </dependency>
    <!--pagehelper paging plug-in jar package dependency-->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>${pagehelper.version}</version>
    </dependency>
    <!--mybatis and spring integrated jar package dependencies-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>${mybatis.spring.version}</version>
    </dependency>

    <!--3.dbcp2 connection pool related-->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-dbcp2</artifactId>
      <version>${commons.dbcp2.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-pool2</artifactId>
      <version>${commons.pool2.version}</version>
    </dependency>

    <!--4.log related dependencies-->
    <!--Core log4j2jar package-->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>${log4j2.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>${log4j2.version}</version>
    </dependency>
    <!--Web projects need to include log4j-web, non-web projects do not need to include -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-web</artifactId>
      <version>${log4j2.version}</version>
    </dependency>

    <!--5.Others-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>${servlet.version}</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!-- jsp dependency -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
    </dependency>
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>

    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.3</version>
    </dependency>

<!-- Do server-side parameter verification JSR303 jar package dependency -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.7.Final</version>
    </dependency>

<!-- Used by SpringMVC to support json data conversion-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.3</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.3</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.3</version>
    </dependency>

<!-- shiro related dependencies -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.3.2</version>
    </dependency>

    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>1.3.2</version>
    </dependency>

    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.3.2</version>
    </dependency>

    <dependency>
      <groupId>net.sf.ehcache</groupId>
      <artifactId>ehcache</artifactId>
      <version>${ehcache.version}</version>
    </dependency>

    <!-- slf4j core package -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j-api.version}</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>jcl-over-slf4j</artifactId>
      <version>${slf4j-api.version}</version>
      <scope>runtime</scope>
    </dependency>

    <!--Used to maintain a bridge with slf4j -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j-impl</artifactId>
      <version>${log4j2.version}</version>
    </dependency>

    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>${redis.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>${redis.spring.version}</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>ssm2</finalName>
    <resources>
      <!--Solve the problem that the XxxMapper.xml file is not placed in the target folder when mybatis-generator-maven-plugin is running-->
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </resource>
      <!--Solve the problem that the jdbc.properites file is not put into the target folder when mybatis-generator-maven-plugin is running-->
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>*.properties</include>
          <include>*.xml</include>
        </includes>
      </resource>
    </resources>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>${maven.compiler.plugin.version}</version>
          <configuration>
            <source>${maven.compiler.source}</source>
            <target>${maven.compiler.target}</target>
            <encoding>${project.build.sourceEncoding}</encoding>
          </configuration>
        </plugin>
        <plugin>
          <groupId>org.mybatis.generator</groupId>
          <artifactId>mybatis-generator-maven-plugin</artifactId>
          <version>1.3.2</version>
          <dependencies>
            <!--Using the Mybatis-generator plug-in cannot use too high a version of mysql driver -->
            <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>${mysql.version}</version>
            </dependency>
          </dependencies>
          <configuration>
            <overwrite>true</overwrite>
          </configuration>
        </plugin>

        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

1.2 spring-redis.xml configuration

This configuration file is mainly used to configure data sources and connection factories as well as configure serialization.

step:

① Register redis.properties

② Configure data source [connection]

③Connect factory

④ Configure serializer

⑤ Configure redis key generation strategy

See the code below for specific steps:

spring-redis.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/cache
       http://www.springframework.org/schema/cache/spring-cache.xsd">
 
    <!-- 1. Introduce properties configuration file -->
    <context:property-placeholder location="classpath:redis.properties" />
 
    <!-- 2. redis connection pool configuration -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!--Maximum idle number-->
        <property name="maxIdle" value="300"/>
        <!--The maximum number of database connections in the connection pool -->
        <property name="maxTotal" value="${redis.maxTotal}"/>
        <!--Maximum waiting time to establish a connection-->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
        <!--Minimum idle time for evicted connections, default 1800000 milliseconds (30 minutes)-->
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}"/>
        <!--The maximum number of evictions during each eviction check. If it is a negative number, it is: 1/abs(n), default 3-->
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}"/>
        <!--The time interval for eviction scanning (milliseconds). If it is a negative number, the eviction thread will not run. The default is -1-->
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}"/>
        <!--Whether to check before taking out the connection from the pool, if the check fails, remove the connection from the pool and try to take out another one-->
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
        <!--Check validity when idle, default false -->
        <property name="testWhileIdle" value="${redis.testWhileIdle}"/>
    </bean>
 
    <!-- 3. redis connection factory -->
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
          destroy-method="destroy">
        <property name="poolConfig" ref="poolConfig"/>
        <!--IP address -->
        <property name="hostName" value="${redis.hostName}"/>
        <!--Port number -->
        <property name="port" value="${redis.port}"/>
        <!--If Redis is set with a password -->
        <property name="password" value="${redis.password}"/>
        <!--The client timeout unit is milliseconds -->
        <property name="timeout" value="${redis.timeout}"/>
    </bean>
 
    <!-- 4. redis operation template, use this object to operate redis
        In the hibernate course, hibernatetemplete is equivalent to session and specializes in operating databases.
    -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
        <!--If Serializer is not configured, String will be used by default when storing. If the User type is used to store, the error User can't cast to String will be prompted! ! -->
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
        </property>
        <!--Open transaction -->
        <property name="enableTransactionSupport" value="true"/>
    </bean>
    <!-- 5. Configure cache manager -->
    <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
        <constructor-arg name="redisOperations" ref="redisTemplate"/>
        <!--redis cache data expiration time unit seconds-->
        <property name="defaultExpiration" value="${redis.expiration}"/>
        <!--Whether to use cache prefix, related to cachePrefix-->
        <property name="usePrefix" value="true"/>
        <!--Configure cache prefix name-->
        <property name="cachePrefix">
            <bean class="org.springframework.data.redis.cache.DefaultRedisCachePrefix">
                <constructor-arg index="0" value="-cache-"/>
            </bean>
        </property>
    </bean>
    <!--6. Configure the generation rules for cache generated key names-->
    <bean id="cacheKeyGenerator" class="com.zking.ssm.redis.CacheKeyGenerator"></bean>
    <!--7. Enable cache annotation function-->
    <cache:annotation-driven cache-manager="redisCacheManager" key-generator="cacheKeyGenerator"/>
</beans>

redis.properties:

redis.hostName=localhost
redis.port=6379
redis.password=123456
redis.timeout=10000
redis.maxIdle=300
redis.maxTotal=1000
redis.maxWaitMillis=1000
redis.minEvictableIdleTimeMillis=300000
redis.numTestsPerEvictionRun=1024
redis.timeBetweenEvictionRunsMillis=30000
redis.testOnBorrow=true
redis.testWhileIdle=true
redis.expiration=3600

1.3 spring context configuration

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx. xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--1. Introduce external multi-file method -->
    <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <property name="ignoreResourceNotFound" value="true" />
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
                <value>classpath:redis.properties</value>
            </list>
        </property>
    </bean>
 
<!-- As you continue to learn, you will learn more and more frameworks. You cannot configure all frameworks in the same preparation room, otherwise it will be inconvenient to manage -->
    <import resource="applicationContext-mybatis.xml"></import>
    <import resource="spring-redis.xml"></import>
    <import resource="applicationContext-shiro.xml"></import>
</beans>

Second, Redis annotation formula

① @Cacheable (can read and write)

@Cacheable is a caching annotation provided by the Spring framework, which is used to mark the return results of methods that can be cached to improve system performance.

as follows:

Test code:

classBiz:

 @Cacheable("clz")
    Clazz selectByPrimaryKey(Integer cid);

Test class text:

 @Test
    public void test1(){
        System.out.println(clazzBiz.selectByPrimaryKey(1));
        System.out.println(clazzBiz.selectByPrimaryKey(1));
    }

The running results will only go to the database once, and then the redis cache will be used, which reduces the pressure on the database

database:

value: a name of the cache location, cannot be empty
key: cached key, empty by default, indicating that the parameter type and parameter value of the method are used as the key, supporting SpEL
condition: Trigger condition. If the condition is met, it will be added to the cache. The default is empty, which means all are added to the cache. SpEL is supported.

② CachePut (read-only data)

The @CachePut annotation is used to update the content in the cache. When a method marked with an annotation is called, Spring will update the value in the cache regardless of whether the corresponding key-value pair already exists in the cache, as follows

classBiz:

 @CachePut(value = "xx",key = "'cid:' + #cid")
 Clazz selectByPrimaryKey(Integer cid);

Test class text:

value: The name of the cache, defined in the spring configuration file, at least one must be specified
key: cached key, which can be empty. If specified, it should be written according to SpEL expression. If not specified, it will be combined according to all parameters of the method by default.
condition: cache condition, can be empty, written in SpEL, returns true or false, only cached if true

@CacheEvict (clear or cache)

@CacheEvict is a cache annotation provided by the Spring framework, which is used to clear the data in the cache after marking the method execution.

 @CacheEvict(value = "xx",key = "'cid:' + #cid",allEntries = true)
    int deleteByPrimaryKey(Integer cid);

value: a name of the cache location, cannot be empty
key: cached key, empty by default, indicating that the parameter type and parameter value of the method are used as the key, supporting SpEL
condition: Trigger condition. If the condition is met, it will be added to the cache. The default is empty, which means all are added to the cache. SpEL is supported.
allEntries: true means clearing all caches in value, the default is false

Three, breakdown, penetration and avalanche of Redis cache

Three common phenomena in caches: Breakdown, penetration, and avalanche

① Breakdown

When the data for a certain key does not exist in the cache, and there are a large number of concurrent requests to access this key, these requests will directly pass through the cache to access the database, causing excessive pressure on the database and even downtime.

Solution:

Use mutex locks or distributed locks to ensure that only one thread accesses the database and other threads wait for the results.

② Penetration

If when requesting data, no data that meets the conditions is found in the cache layer and database layer, that is, no data is hit in the cache layer and database layer, then this situation is called cache penetration.

Solution:

Preset the null value or default value corresponding to this key in the cache to avoid direct access to the database by a large number of requests.

legend:

③ Avalanche

When a large amount of data in the cache fails at the same time and is accessed by a large number of requests, these requests will directly access the database, causing excessive pressure on the database or even downtime.

Solution:

– The expiration time of cached data is set randomly to avoid the simultaneous expiration of a large amount of data.
– Use a multi-level cache architecture to avoid single points of failure.
– Use the circuit breaker mechanism to temporarily block access to the database when the cache fails to avoid excessive pressure.

legend:

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. MySQL entry-level skills treeDatabase compositionTable 77675 people are learning the system