关闭

2 Springboot中使用redis,配置redis的key value生成策略

标签: spring boot 整合redisredis如何做条件查询springboot缓存
9189人阅读 评论(1) 收藏 举报
分类:

上一篇里讲过了redis在spring boot中的简单使用,对于单个对象的增删改查的默认操作。

下面来看一下在redis中,这些缓存的数据是如何存储的,为了便于后面的缓存的key的可读性,先修改一下cache的key。

@CacheConfig(cacheNames = "post")
public interface PostRepository extends PagingAndSortingRepository<Post, Integer> {
    @Cacheable(key = "'PostId' + #p0")
    Post findById(int id);

    /**
     * 新增或修改时
     */
    @CachePut(key = "'PostId' + #p0.id")
    @Override
    Post save(Post post);

    @Transactional
    @Modifying
    @CacheEvict(key = "'PostId' + #p0")
    int deleteById(int id);
}
给key上加个字符串postId,用类似于postId3作为key,整个Post对象作为value。调用controller的save接口添加一条Post数据,打开redis可视化管理器,查看一下保存的这条数据:

发现key是以post:XX开头的乱码形式。这是默认的key生成策略,是通过序列化Serializable后生成的key,当读取缓存时系统再通过反序列化得到Post对象。

如果我们想修改序列化方式,来生成一个可读的key和value,下面是方法。

譬如如果key我想用字符串如 PostId1,value为Post对象转成的Json对象:

package com.tianyalei.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Created by wuwf on 17/4/24.
 */
@Configuration
public class RedisCacheConfig {

    @Bean
    public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {
        CacheManager cacheManager = new RedisCacheManager(redisTemplate);
        return cacheManager;

    }

    @Bean
    public RedisTemplate<String, String> getRedisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        //key序列化方式,但是如果方法上有Long等非String类型的话,会报类型转换错误
        //所以在没有自己定义key生成策略的时候,以下这个代码建议不要这么写,可以不配置或者自己实现ObjectRedisSerializer
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long类型不可以会出现异常信息;
        redisTemplate.setKeySerializer(redisSerializer);
//        redisTemplate.setHashKeySerializer(redisSerializer);
//        redisTemplate.setValueSerializer(redisSerializer);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }

}
上面这个类主要是定制RedisTemplate的KeySerializer和ValueSerializer。其中StringRedisSerializer和Jackson2JsonRedisSerializer都是系统提供的已实现的序列化方式。

StringXX是转为String,JacksonXX是将对象转为json。需要注意这里Key使用了StringRedisSerializer,那么Key只能是String类型的,不能为Long,Integer,否则会报错抛异常。就是假如PostRepository里定义的@Cacheable(key="#p0")的话就会报错,因为这样作为key的是int型,key必须为String。

上面的方法就是设置了key和value的序列化方式,然后返回默认的RedisTemplate。RedisTemplate有几个默认的实现类,常用的如StringRedisTemplate就是提供的RedisTemplate<String, String>的实现。可以参考下面的文章简单了解下StringRedisTemplate。

http://blog.didispace.com/springbootredis/和http://blog.csdn.net/fengzheku/article/details/49735785

StringRedisTemplate其实就是使用StringRedisSerializer对key,value设置序列化。


当然也可以自己定义序列化方式,使用别的Json工具类,或者别的什么方法来完成序列化方式。
完成RedisTemplate的设置后,再次save一个Post对象来看看在redis里的存储方式。

可以看到PostId12就是刚添加成功对象,key为PostId12,即是PostResposity里配置的key,value为Json字符串和一个类名。

然后还多了一个post~keys的zset对象,里面存放的是key。

通过上面的配置,我们就完成对序列化方式自定义的配置,尤其是key的定制,能方便日后的查看以及在别的地方操作key时更易识别。

在上一篇里,还提到了无需配置yml中redis的属性,ip、port之类的,系统会识别默认的。下面来看看如何使用自己的redis配置。

修改yml文件:

spring:
  jpa:
    database: mysql
    show-sql: true
    hibernate:
      ddl-auto: update
  datasource:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/tx2
      username: root
      password:
  redis:
      host: localhost
      port: 6379
      password:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: 10000
这里面加入了redis 的配置。可以用ctrl加左键点击host或者post属性,进入类。



这个就是采用prefix=spring.redis前缀的配置类,我们也可以自定义类似的配置类。

在配置文件里设置了ip和port及pool等属性,然后打开RedisCacheConfig类,来使用yml里的这些redis配置。

package com.tianyalei.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Created by wuwf on 17/4/24.
 */
@Configuration
public class RedisCacheConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.pool.max-active}")
    private int maxActive;
    @Value("${spring.redis.pool.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.pool.min-idle}")
    private int minIdle;
    @Value("${spring.redis.pool.max-wait}")
    private int maxWait;

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setPassword(password);
        factory.setHostName(host);
        factory.setPort(port);
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxActive);
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWait);
        factory.setPoolConfig(jedisPoolConfig);
        return factory;
    }
    
    @Bean
    public RedisTemplate<String, String> getRedisTemplate() {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        //key序列化方式,但是如果方法上有Long等非String类型的话,会报类型转换错误
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long类型不可以会出现异常信息;
        redisTemplate.setKeySerializer(redisSerializer);
//        redisTemplate.setHashKeySerializer(redisSerializer);
//        redisTemplate.setValueSerializer(redisSerializer);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }

}

配置文件里的内容主要目的就是为了配置JedisConnectionFactory,这里我们使用配置文件定义的属性来创建一个自己的JedisConnectionFactory。然后在创建RedisTemplate时使用这个自定义的JedisConnectionFactory即可。

这样就完成了redis的自定义信息,以后就可以使用RedisTemplate来操作redis了。可以通过修改yml里的连接信息来看看是否已生效。

如果觉得上面使用自定义配置的步骤复杂,可以使用简单方式,如下

@Bean
    public RedisTemplate<String, String> getRedisTemplate(JedisConnectionFactory jedisConnectionFactory) {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        //key序列化方式,但是如果方法上有Long等非String类型的话,会报类型转换错误
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();//Long类型不可以会出现异常信息;
        redisTemplate.setKeySerializer(redisSerializer);
//        redisTemplate.setHashKeySerializer(redisSerializer);
//        redisTemplate.setValueSerializer(redisSerializer);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }

该类只保留这一个方法就可以了,方法里加上参数JedisConnectionFactory,然后直接使用就行。上面定义的那些配置会被框架自动解释到这个参数里。效果和自己手工创建JedisConnectionFactory并设置参数是一样的。


该篇到此为止,下面还有几个问题需要考虑:

1.怎么处理db操作成功了,但操作redis失败。譬如刚才修改一下yml的ip地址,让redis连接不上,那么对db的操作还是会成功,但redis数据就不对了。

2.怎么操作集合数据,因为一个key对应一个集合转化的json字符串,是无法单独添加一条对象数据的,只能全失效或全成功,这样的话就不适合存储频繁改变的集合数据。

下篇来看看这些问题。


3
0
查看评论

关于Spring Data redis几种对象序列化的比较

GenericToStringSerializer: 可以将任何对象泛化为字符串并序列化 Jackson2JsonRedisSerializer: 跟JacksonJsonRedisSerializer实际上是一样的 JacksonJsonRedisSerializer: 序列化ob...
  • hotdust
  • hotdust
  • 2016-07-05 17:09
  • 8180

Spring Boot Cache redis 序列化方式StringRedisSerializer和FastJsonRedisSerializer

当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是String...
  • xiaolyuh123
  • xiaolyuh123
  • 2017-11-30 23:14
  • 482

spring data redis serializer SerializationException 序列化问题

speing data redis serializer SerializationException 序列化问题 项目中需要使用redis做一些缓存失效,以达到验证码失效的目的。由于K,V使用了,,验证验证码是否存在,是否达到规定次数。参看了官方文档(内容很少),碰到了序列化的问题,异常如下:...
  • doctor_who2004
  • doctor_who2004
  • 2015-09-28 21:40
  • 15883

使用Spring Cache + Redis + Jackson Serializer缓存数据库查询结果中序列化问题的解决

应用场景我们希望通过缓存来减少对关系型数据库的查询次数,减轻数据库压力。在执行DAO类的select***(), query***()方法时,先从Redis中查询有没有缓存数据,如果有则直接从Redis拿到结果,如果没有再向数据库发起查询请求取数据。序列化问题要把domain object做为key...
  • tracker_w
  • tracker_w
  • 2015-07-08 10:32
  • 14092

spring-data-redis key-value序列化

spring-data-redis key-value序列化需要注意的问题
  • hudi10356
  • hudi10356
  • 2014-11-13 10:25
  • 3864

Spring Redis(3)序列化

序列化Spring Data Redis支持JDK序列化、Json序列化、XML序列化 - JDK序列化:默认采用JDK序列化方式的类JdkSerializationRedisSerializer,速度快但占用空间较大,对象必须实现java.io.Serializable接口 - Json序列化...
  • supermancoke
  • supermancoke
  • 2017-04-11 14:55
  • 2506

使用Spring Data Redis时,遇到的几个问题

需求: 1,保存一个key-value形式的结构到redis 2,把一个对象保存成hash形式的结构到redis
  • hotdust
  • hotdust
  • 2016-08-23 17:33
  • 8098

Redis序列化

redis序列化数据有多重方式: JacksonJsonRedisSerializer JdkSerializationRedisSerializer OxmSerializer 这里我用前两种测试1.StringSerializer.javapublic enum StringSerializer...
  • mxj588love
  • mxj588love
  • 2016-12-06 14:11
  • 714

SpringBoot整合Redis(附带序列化方式对比)

一、缓存数据库性能Redis、memcached、Ehcache Redis存储数据类型丰富,对于存储数据量不是很大的情况下处理性能效果较好、支持持久化 memcached对于大量的数据存储和读取性能要优于Redis、没有持久化 Ehcache最大的特点是轻量级,而且存储的数据类型为对象 二...
  • WALK_MAN_wubiao
  • WALK_MAN_wubiao
  • 2018-01-27 20:58
  • 90

缓存key生成策略的一些思考

首选说说策略设计目标: 1、唯一性保证 2、方便获取 3、高效性 MD5码等编码策略: 效率太低,
  • flashflight
  • flashflight
  • 2016-05-17 01:18
  • 7501
    个人资料
    • 访问:353112次
    • 积分:4412
    • 等级:
    • 排名:第8123名
    • 原创:100篇
    • 转载:39篇
    • 译文:0篇
    • 评论:148条
    博客专栏
    友情链接
    最新评论