缓存

缓存其实就是一个临时的存储器。

为什么使用缓存

客户端第一次请求的时候是从库里拿出我们需要的数据,但如果每次查询都从库里拿,就会很耗时耗能。那么使用缓存以后,我们只需要第一次从库里拿完存到缓存中,只要不清除缓存,我们以后的请求都直接在缓存中拿数据,就会快很多很多

缓存作用

缓存主要是为了提高数据的读取速度。 因为服务器和应用客户端之间存在着流量的瓶颈,所以读取大容量数据时,使用缓存来直接为客户端服务,可以减少客户端与服务器端的数据交互,从而大大提高程序的性能。

Springboot缓存

SpringBoot支持很多种缓存方式:redis、guava、ehcahe、jcache等等。

缓存注解详解

@CacheConfig

主要用于配置该类中会用到的一些共用的缓存配置。 在这里@CacheConfig(cacheNames = “users”):配置了该数据访问对象中返回的内容将存储于名为users的缓存对象中,我们也可以不使用该注解,直接通过@Cacheable自己配置缓存集的名字来定义。

@Cacheable

配置了findByName函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。
注解参数

  • value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存组件的名字,数组形式。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
  • key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:编写SpEL; #id,参数id的值 ,,#a0(第一个参数), #p0(和a0的一样的意义) ,#root.args[0]
  • condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存,若做此配置上面的AAA用户就不会被缓存,读者可自行实验尝试。
  • unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
  • keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:key/keyGenerator:二选一使用;不能同时使用
  • cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
  • cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。
  • sync:是否使用异步模式
@CachePut

配置于函数上,能够根据参数定义条件来进行缓存,它与@Cacheable不同的是,它每次都会真是调用函数,所以主要用于数据新增和修改操作上。 它的参数与@Cacheable类似,具体功能可参考上面对@Cacheable参数的解析

@CacheEvict

配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。也就是清除缓存除了同@Cacheable一样的参数之外,它还有下面两个参数:

  • allEntries:非必需,默认为false。当为true时,会移除所有数据
  • beforeInvocation:非必需,**默认为false,会在调用方法之后移除数据。**当为true时,会在调用方法之前移除数据。
@Caching注解

用于定义复杂的缓存规则,可以集成@Cacheable和 @CachePut

// @Caching 定义复杂的缓存规则
    @Caching(
            cacheable = {
                    @Cacheable(/*value={"emp"},*/key = "#lastName")
            },
            put = {
                    @CachePut(/*value={"emp"},*/key = "#result.id"),
                    @CachePut(/*value={"emp"},*/key = "#result.email")
            }
    )
    public Employee getEmpByLastName(String lastName){
        return employeeMapper.getEmpByLastName(lastName);
    }

SpEL表达式用法

在这里插入图片描述

Spring Boot 整合 Ehcache

什么是Ehcache?

Java缓存框架 EhCache EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。

主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

整合步骤

1、修改 pom 文件
引入Ehcache jar包

<!-- Ehcache 坐标 -->
<dependency>
	<groupId>net.sf.ehcache</groupId>
	<artifactId>ehcache</artifactId>
</dependency>

2、创建 Ehcache 的配置文件
文件名:ehcache.xml

位置:src/main/resources/ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<diskStore path="java.io.tmpdir" />
	<!--defaultCache:echcache 的默认缓存策略 -->
	<defaultCache maxElementsInMemory="10000" eternal="false"
		timeToIdleSeconds="120" timeToLiveSeconds="120"
		maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
		<persistence strategy="localTempSwap" />
	</defaultCache>
	<!-- 自定义缓存策略 -->
	<cache name="users" maxElementsInMemory="10000" eternal="false"
		timeToIdleSeconds="120" timeToLiveSeconds="120"
		maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
		<persistence strategy="localTempSwap" />
	</cache>
</ehcache> 

3、修改 application.properties 文件
主要添加 ehcache 的配置xml文件的位置

spring.cache.ehcache.cofnig=ehcache.xml 

4、修改启动类
启动类上在增加 @EnableCaching,作用就是启动 SpringCache 功能,使缓存生效 。

5、在service业务层用注解来使用缓存
也可以在dao层使用注解,使用方式一样。注意:这里就算不调用查询数据库的方法,service层的方法返回值也会被缓存,也就是该缓存注解缓存的是方法的返回值,与方法里的执行逻辑无关。

@Service
public class UsersServiceImpl implements UsersService {
	@Autowired
	private UsersRepository usersRepository;

	@Override
	// @Cacheable:对当前查询的对象做缓存处理
	@Cacheable(value = "users")
	public Users findUserById(Integer id) {
		return this.usersRepository.findOne(id);
	
}

Springboot整合Redis

整合步骤

1、引入依赖 pom.xml

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

2、修改application.yml,配置redis

#数据库连接
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mytest_springboot_cache?useUnicode=true
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: lzh
 
  ## Redis 配置
  redis:
    ## Redis数据库索引(默认为0)
    database: 0
    ## Redis服务器地址
    host: 192.168.126.129
    ## Redis服务器连接端口
    port: 6379
    ## Redis服务器连接密码(默认为空)
    password:
    jedis:
      pool:
        ## 连接池最大连接数(使用负值表示没有限制)
        #spring.redis.pool.max-active=8
        max-active: 8
        ## 连接池最大阻塞等待时间(使用负值表示没有限制)
        #spring.redis.pool.max-wait=-1
        max-wait: -1
        ## 连接池中的最大空闲连接
        #spring.redis.pool.max-idle=8
        max-idle: 8
        ## 连接池中的最小空闲连接
        #spring.redis.pool.min-idle=0
        min-idle: 0
    ## 连接超时时间(毫秒)
    timeout: 1200

3、编写java配置类

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
 
    /**
     * 选择redis作为默认缓存工具
     * @param redisConnectionFactory
     * @return
     */
    /*@Bean
    //springboot 1.xx
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
        return rcm;
    }*/
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时
        return RedisCacheManager
                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(redisCacheConfiguration).build();
    }
 
    /**
     * retemplate相关配置
     * @param factory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
 
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);
 
        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);
 
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(om);
 
        // 值采用json序列化
        template.setValueSerializer(jacksonSeial);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
 
        // 设置hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jacksonSeial);
        template.afterPropertiesSet();
 
        return template;
    }
 
    /**
     * 对hash类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }
 
    /**
     * 对redis字符串类型数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForValue();
    }
 
    /**
     * 对链表类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }
 
    /**
     * 对无序集合类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }
 
    /**
     * 对有序集合类型的数据操作
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}

这里主要是使用RedisTemplate来对远程redis操作,每次访问controller暴露的接口,首先判断redis缓存中是否存在该数据,若不存在就从数据库中读取数据,然后保存到redis缓存中,当下次访问的时候,就直接从缓存中取出来。这样就不用每次都执行sql语句,能够提高访问速度。 但是在保存数据到缓存中,通过设置键和值和超时删除,注意设置超时删除缓存时间不要太长,否则会给服务器带来压力。

也可以使用注解的形式,使用方式和Ehcache的使用方式一致。

Springboot整合redis集群

这里不介绍redis集群,详细见redis类型的博客。

不指定redis连接池

yml配置文件:

spring:
  redis:
    cluster:
      nodes:
        - 192.168.1.236:7001
        - 192.168.1.236:7002
        - 192.168.1.236:7003
        - 192.168.1.244:7004
        - 192.168.1.244:7005
        - 192.168.1.244:7006
      max-redirects: 3  # 获取失败 最大重定向次数
    pool:
      max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
      max-idle: 10    # 连接池中的最大空闲连接
      max-wait: -1   # 连接池最大阻塞等待时间(使用负值表示没有限制)
      min-idle:  5     # 连接池中的最小空闲连接
    timeout: 6000  # 连接超时时长(毫秒)

这种方式 redisTemplate 可直接使用默认,在使用的地方直接注入即可

@Autowired
private RedisTemplate<String, Object> redisTemplate;

使用jedis连接池

yml配置文件:

spring:
  redis:
    password:    # 密码(默认为空)
    timeout: 6000ms  # 连接超时时长(毫秒)
    cluster:
      nodes:
        - 192.168.1.236:7001
        - 192.168.1.236:7002
        - 192.168.1.236:7003
        - 192.168.1.244:7004
        - 192.168.1.244:7005
        - 192.168.1.244:7006 
    jedis:
      pool:
        max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms      # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 10      # 连接池中的最大空闲连接
        min-idle: 5       # 连接池中的最小空闲连接

连接池注入配置信息

@Configuration
public class RedisConfig {
   @Autowired
   private RedisConnectionFactory factory;
 
   @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }
}

在使用的地方直接注入即可

@Autowired
private RedisTemplate<String, Object> redisTemplate;

使用lettuce连接池(推荐)

yml配置文件:

spring:
  redis:
    timeout: 6000ms
    password: 
    cluster:
      max-redirects: 3  # 获取失败 最大重定向次数 
      nodes:
        - 192.168.1.236:7001
        - 192.168.1.236:7002
        - 192.168.1.236:7003
        - 192.168.1.244:7004
        - 192.168.1.244:7005
        - 192.168.1.244:7006 
    lettuce:
      pool:
        max-active: 1000  #连接池最大连接数(使用负值表示没有限制)
        max-idle: 10 # 连接池中的最大空闲连接
        min-idle: 5 # 连接池中的最小空闲连接
        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)

连接池注入配置信息

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {   
    @Bean
    public RedisTemplate<String, Object> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

在使用的地方直接注入即可

@Autowired
private RedisTemplate<String, Object> redisTemplate;
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐