redis spring cache

数据库的数据是存储在硬盘上的,频繁访问性能较低。如果将一些需要频繁查询的热数据放到内存的缓存中,可以大大减轻数据库的访问压力。

SpringCache

SpringCache提供基本的Cache抽象,并没有具体的缓存能力,需要配合具体的缓存实现来完成,目前SpringCache支持redis、ehcache、simple(基于内存)等方式来实现缓存。
org.springframework.boot spring-boot-starter-cache

1
2
3
4

Spring常用的缓存注解

非常好的参考文章

SpringBoot基础系列-SpringCache使用

spring boot2 (28)-cache缓存

SpringBoot2.x---SpringCache(2)使用

通过继承CachingConfigurerSupport类自定义缓存管理器【与本文方式不同,值得一看】

@EnableCaching:开启缓存功能,一般使用在springboot的启动类或配置类上
@Cacheable:使用缓存。在方法执行前Spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;没有则调用方法并将方法返回值放进缓存。

@Cacheable属性名 用途 备注

cacheNames 指定缓存空间的名称,不同缓存空间的数据是彼此隔离的

key 同一个cacheNames中通过key区别不同的缓存。如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合,如:@CachePut(value = "demo", key = "'user'+#user.id"),字符串中spring表达式意外的字符串部分需要用单引号 SpringCache提供了与缓存相关的专用元数据,如target、methodMame、result、方法参数等,如:@CachePut(value = "demo", key = "#result==null")

keyGenrator key的生成策略,SpringCache默认使用SimpleKeyGenerator,默认情况下将参数值作为键,但是可能会导致key重复出现,因此一般需要自定义key的生成策略

condition condition是在调用方法之前判断条件,满足条件才缓存 @Cacheable(cacheNames="book", condition="#name.length() < 32")

unless unless是在调用方法之后判断条件,如果SpEL条件成立,则不缓存【条件满足不缓存】 @Cacheable(cacheNames = "hello",unless="#result.id.contains('1')" )

sync 缓存过期之后,如果多个线程同时请求对某个数据的访问,会同时去到数据库,导致数据库瞬间负荷增高。Spring4.3为@Cacheable注解提供了一个新的参数"sync"(boolean类型,默认为false)。当设置它为true时,只有一个线程的请求会去到数据库,其他线程都会等待直到缓存可用。这个设置可以减少对数据库的瞬间并发访问。

@CachePut:更新缓存,将方法的返回值放到缓存中

//当返回值为null时,通过unless属性

@CachePut(key = "#id", unless="#result == null")

1
2

@CacheEvict:清空缓存
@Caching:组合定义多种缓存功能
@CacheConfig:用于标注在类上,可以存放该类中所有缓存的公有属性,比如设置缓存空间的名字cacheNames、key生成策略keyGenerator、缓存管理器cacheManager

SpringCache采用Redis实现缓存

依赖

org.springframework.boot spring-boot-starter-cache 2.3.0.RELEASE org.springframework.boot spring-boot-starter-data-redis 2.3.0.RELEASE lettuce-core io.lettuce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

配置yaml文件

spring:

redis:

database: 0

host: www.onething.top

port: 6379

password: 123456nw

#配置jedis客户端,这里也可以jedis替换为lettuce客户端,下级配置都一样

jedis:

pool:

连接池中的最大空闲连接 默认8

max-idle: 8

连接池中的最小空闲连接 默认0

min-idle: 0

连接池最大连接数 默认8 ,负数表示没有限制

max-active: 8

连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1

max-wait: -1

timeout: 3000

cache:

type: redis # 指定使用的缓存类型

redis: 当自定义ChacheManager时,就这里的配置不需要配置,配置了也不起作用

use-key-prefix: true

key-prefix: "demo:"

time-to-live: 60000 #缓存超时时间 单位:ms

cache-null-values: false #是否缓存空值

cache-names: user

cache:

ttl: '{"user":60,"dept":30}' #自定义某些缓存空间的过期时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

自定义ChacheManager、缓存key的生成策略,将默认的jdk序列化策略修改为json序列化策略

@Configuration

public class RedisConfiguration {

// ${cache} 获取配置文件的配置信息   #{}是spring表达式,获取Bean对象的属性
@Value("#{${cache}}")
private Map<String, Long> ttlParams;

/**
 * @param redisConnectionFactory
 * @功能描述 redis作为缓存时配置缓存管理器CacheManager,主要配置序列化方式、自定义
 * <p>
 * 注意:配置缓存管理器CacheManager有两种方式:
 * 方式1:通过RedisCacheConfiguration.defaultCacheConfig()获取到默认的RedisCacheConfiguration对象,
 * 修改RedisCacheConfiguration对象的序列化方式等参数【这里就采用的这种方式】
 * 方式2:通过继承CachingConfigurerSupport类自定义缓存管理器,覆写各方法,参考:
 * https://blog.csdn.net/echizao1839/article/details/102660649
 * <p>
 * 切记:在缓存配置类中配置以后,yaml配置文件中关于缓存的redis配置就不会生效,如果需要相关配置需要通过@value去读取
 */
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
    redisCacheConfiguration = redisCacheConfiguration
            // 设置key采用String的序列化方式
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer.UTF_8))
            //设置value序列化方式采用jackson方式序列化
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer()))
            //当value为null时不进行缓存
            .disableCachingNullValues()
            // 配置缓存空间名称的前缀
            .prefixCacheNameWith("demo:")
            //全局配置缓存过期时间【可以不配置】
            .entryTtl(Duration.ofMinutes(30L));
    //专门指定某些缓存空间的配置,如果过期时间【主要这里的key为缓存空间名称】
    Map<String, RedisCacheConfiguration> map = new HashMap<>();
    Set<Map.Entry<String, Long>> entries = ttlParams.entrySet();
    for (Map.Entry<String, Long> entry : entries) {
        //指定特定缓存空间对应的过期时间
        map.put("user", redisCacheConfiguration.entryTtl(Duration.ofSeconds(40)));
        map.put(entry.getKey(), redisCacheConfiguration.entryTtl(Duration.ofSeconds(entry.getValue())));
    }
    return RedisCacheManager
            .builder(redisConnectionFactory)
            .cacheDefaults(redisCacheConfiguration)  //默认配置
            .withInitialCacheConfigurations(map)  //某些缓存空间的特定配置
            .build();
}


/**
 * 自定义缓存的redis的KeyGenerator【key生成策略】
 * 注意: 该方法只是声明了key的生成策略,需在@Cacheable注解中通过keyGenerator属性指定具体的key生成策略
 * 可以根据业务情况,配置多个生成策略
 * 如: @Cacheable(value = "key", keyGenerator = "cacheKeyGenerator")
 */
@Bean
public KeyGenerator keyGenerator() {
    /**
     * target: 类
     * method: 方法
     * params: 方法参数
     */
    return (target, method, params) -> {
        //获取代理对象的最终目标对象
        StringBuilder sb = new StringBuilder();
        sb.append(target.getClass().getSimpleName()).append(":");
        sb.append(method.getName()).append(":");
        //调用SimpleKey的key生成器
        Object key = SimpleKeyGenerator.generateKey(params);
        return sb.append(key);
    };
}


/**
 * @param redisConnectionFactory:配置不同的客户端,这里注入的redis连接工厂不同: JedisConnectionFactory、LettuceConnectionFactory
 * @功能描述 :配置Redis序列化,原因如下:
 * (1) StringRedisTemplate的序列化方式为字符串序列化,
 * RedisTemplate的序列化方式默为jdk序列化(实现Serializable接口)
 * (2) RedisTemplate的jdk序列化方式在Redis的客户端中为乱码,不方便查看,
 * 因此一般修改RedisTemplate的序列化为方式为JSON方式【建议使用GenericJackson2JsonRedisSerializer】
 */
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = serializer();
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    // key采用String的序列化方式
    redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8);
    // value序列化方式采用jackson
    redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
    // hash的key也采用String的序列化方式
    redisTemplate.setHashKeySerializer(StringRedisSerializer.UTF_8);
    //hash的value序列化方式采用jackson
    redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    return redisTemplate;
}

/**
 * 此方法不能用@Ben注解,避免替换Spring容器中的同类型对象
 */
public GenericJackson2JsonRedisSerializer serializer() {
    return new GenericJackson2JsonRedisSerializer();
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

应用

(1)开启缓存功能

@EnableCaching //开启缓存的主键

@SpringBootApplication

public class RedisDemoApplication {

public static void main(String[] args) {

SpringApplication.run(RedisDemoApplication.class, args);

}

}

1
2
3
4
5
6
7

(2)使用缓存

@Service

@CacheConfig(cacheNames = "user",keyGenerator = "keyGenerator")

public class RedisServiceImpl implements RedisService {

@Cacheable(value = "user", key = "'list'")
@Override
public List<User> list() {
    System.out.println("=========list");
    User user1 = new User();
    user1.setId(1);
    user1.setName("老大");
    User user2 = new User();
    user2.setId(2);
    user2.setName("老二");
    List<User> users = new ArrayList<>();
    users.add(user1);
    users.add(user2);
    return users;
}

@CacheEvict(value = "user", key = "'list'")
@Override
public void del(Integer id) {
    System.out.println("************************************+id");
    List<User> users = new ArrayList<>();
    Iterator<User> iterator = users.iterator();
    while (iterator.hasNext()) {
        User user = iterator.next();
        if (user.getId().equals(id)) {
            iterator.remove();
            break;
        }
    }
}

@CachePut(value = "demo", key = "#result==null")
@Override
public User select(Integer id) {
    System.out.println("===============dddd================");
    if (id == 0) {
        return null;
    }
    User user = new User();
    user.setId(100);
    user.setName("测试");
    return user;
}

}


                        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/user2025/article/details/106595257

https://blog.csdn.net/user2025/article/details/106595257?spm=1001.2101.3001.6650.16\&utm_medium=distribute.pc_relevant.none-task-blog-2~default~OPENSEARCH~Rate-16-106595257-blog-135942825.235^v43^control\&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~OPENSEARCH~Rate-16-106595257-blog-135942825.235^v43^control\&utm_relevant_index=17

相关推荐
王佑辉4 分钟前
【redis】redis缓存和数据库保证一致性的方案
redis·面试
小码的头发丝、34 分钟前
Django中ListView 和 DetailView类的区别
数据库·python·django
Karoku06643 分钟前
【企业级分布式系统】Zabbix监控系统与部署安装
运维·服务器·数据库·redis·mysql·zabbix
鹿屿二向箔1 小时前
基于SSM(Spring + Spring MVC + MyBatis)框架的汽车租赁共享平台系统
spring·mvc·mybatis
豪宇刘1 小时前
SpringBoot+Shiro权限管理
java·spring boot·spring
gorgor在码农1 小时前
Redis 热key总结
java·redis·热key
想进大厂的小王1 小时前
项目架构介绍以及Spring cloud、redis、mq 等组件的基本认识
redis·分布式·后端·spring cloud·微服务·架构
Java 第一深情1 小时前
高性能分布式缓存Redis-数据管理与性能提升之道
redis·分布式·缓存
周全全1 小时前
MySQL报错解决:The user specified as a definer (‘root‘@‘%‘) does not exist
android·数据库·mysql
白云如幻1 小时前
MySQL的分组函数
数据库·mysql