springboot学习(十):Redis的使用

说明

通过本篇文章记录总结下在springboot中使用redis,还有在工作中使用redis时碰到的一些问题。

正文

引入依赖

新建springboot项目,引入redis依赖

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

配置redis

这里有两种配置方式,一种是通过在application.properties中直接配置redis,另一种就是通过创建redis.propertiesp配置文件。

创建redis.properties

redis.server.host=localhost
redis.server.port=6379
reids.server.timeout=2000

redis.pool.maxIdle=50
redis.pool.minIdle=1
redis.pool.maxWait=1000
redis.pool.maxTotal=-1
redis.pool.testWhileIdle=true

创建配置的的相关类RedisConn和RedisConfig

RedisConn
public class RedisConn {

    private String host;
    private int port;
    private int timeout;
    ....省略setter和getter
}
RedisConfig
@Configuration
@PropertySource(value = "redis.properties")
public class RedisConfig {

    @Bean
    @ConfigurationProperties(prefix = "redis.pool")
    public JedisPoolConfig jedisPoolConfig(){
        return new JedisPoolConfig();
    }

    @Bean
    @ConfigurationProperties(prefix = "redis.server")
    public RedisConn redisConn(){
        return new RedisConn();
    }

    @Bean
    public JedisConnectionFactory jedisConnectionFactory(@Qualifier("jedisPoolConfig") JedisPoolConfig jedisPoolConfig,
                                                         @Qualifier("redisConn") RedisConn redisConn){
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setPoolConfig(jedisPoolConfig);
        jedisConnectionFactory.setHostName(redisConn.getHost());
        jedisConnectionFactory.setPort(redisConn.getPort());
        jedisConnectionFactory.setTimeout(redisConn.getTimeout());

        return jedisConnectionFactory;

    }


    @Bean(name = "redisTemplate")
    public RedisTemplate<String,Object> redisTemplate(@Qualifier("jedisConnectionFactory") JedisConnectionFactory jedisConnectionFactory){
        RedisTemplate<String,Object> template = new RedisTemplate<>();
        template.setConnectionFactory(jedisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new StringRedisSerializer());
        template.setValueSerializer(new StringRedisSerializer());

        return template;
    }
}

在application.properties中配置

#Redis数据库的默认索引 默认为0
spring.redis.database=0
#Redis服务器地址
spring.redis.host=localhost
#Redis服务器连接端口
spring.redis.port=6379
#Redis服务器连接密码(默认为空)
spring.redis.password=
#连接池的最大连接数 (使用负值表示没有限制)
spring.redis.pool.max-active=8
#连接池最大阻塞时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
#连接池中最大的空闲连接
spring.redis.pool.max-idle=8
#连接池中最小的空闲连接
spring.redis.pool.min-idle=0
#连接超时时间(毫秒)
spring.redis.timeout=1000

Redis的使用

通过上述配置,我们可以通过RedisTemplate来对redis操作,但是存储的value都是String类型的,如果要存储一个对象,就必须将其转为json或实现关于对象的RedisSerializer。这里通过创建ObjectSerializer来实现对象的存取。

ObjectSerializer

public class ObjectSerializer implements RedisSerializer<Object> {
    private Converter<Object,byte[]> serializer = new SerializingConverter();
    private Converter<byte[],Object> deserializer = new DeserializingConverter();

    static final byte[] EMPTY_APPAY = new byte[0];

    @Override
    public byte[] serialize(Object o) throws SerializationException {
        if(o == null){
            return EMPTY_APPAY;
        }
        return serializer.convert(o);
    }

    @Override
    public Object deserialize(byte[] bytes) throws SerializationException {
        if(isEmpty(bytes)){
            return null;
        }
        return deserializer.convert(bytes);
    }
}

创建好类后需要对其进行配置,在RedisConfig中配置:

 @Bean(name = "userRedisTemplate")
 public RedisTemplate<String, User> userRedisTemplate(@Qualifier("jedisConnectionFactory") JedisConnectionFactory jedisConnectionFactory){
      RedisTemplate<String,User> template = new RedisTemplate<>();
      template.setConnectionFactory(jedisConnectionFactory);
      template.setKeySerializer(new StringRedisSerializer());
      template.setValueSerializer(new ObjectSerializer());

      return template;
  }

测试:

@Test
public void testRedisUser(){
	User user = new User();
	user.setName("zhangsan");
	user.setAge(23);
	user.setGender("male");
	userRedisTemplate.opsForValue().set(user.getName(),user,3600,TimeUnit.SECONDS);
}

相关问题

在开发中,使用redis的zset来实现某个排行需求,但是在内测过程中发现相同score的数据的时间信息对不上,在开发时直接使用了sort sets的revrange命令来得到数据的逆序,通过查询资料,该命令会根据index和score来对数据逆序,也就是相同score的情况下,最新添加的数据通过revrange命令得到的排序会在相同score的第一位。但实际情况是相同分数的时间是错乱的,而且时间差很大。
在遇到这个问题时,以为是命令的问题,通过jedis来连接redis并操作数据,查看源码后发现,jedis的revrange命令的结果居然是使用set类型接收。想到排序后的数据再放入set集合,肯定会导致数据排序失效,(心想这是不是jedis的bug…)但是经过大量的测试,返回的数据没有任何问题,也就是jedis在这个命令使用set接收结果没有任何问题(好吧…)又重新去看源码,发现此set并非彼set,就是一个披着set外衣的list…应该是jedis为了返回类型与数据类型相统一做了一层封装。
jedis使用SetFromList类来封装结果,使用SetFromList.of(List<E> list)方法封装。
SetFromList类继承了AbstractSet,但是内部有真正存储数据的成员变量的list :

protected static class SetFromList<E> extends AbstractSet<E> implements Serializable {
        private static final long serialVersionUID = -2850347066962734052L;
        private final transient List<E> list;

        private SetFromList(List<E> list) {
            if (list == null) {
                throw new NullPointerException("list");
            } else {
                this.list = list;
            }
        }
        ......
}

所以revrange返回的set只是一个内部有真正存储数据list的外壳。阅读完源码后清楚命令没有问题,那到底是什么原因导致的数据的时间错乱,之后又进行了简单的测试,这次从数据的添加入手,使用for循环顺序添加100个数据 value为0-99,score相同,结果发现value的排序并没有以0-99的顺序出现,出现了顺序错乱。我猜测应该是在并发情况下,redis是单线程操作,导致数据的实际添加顺序并不是按照调用方法的顺序。但是为什么数据时间差会那么大相差几个小时?

解决数据时间错乱的方法

将revrange得到数据再次排序,根据value(id)的值得到用户相关数据后,根据用户的提交时间进行降序,再根据用户的得分进行降序,这样就得到用户得分排行榜,并且在具有相同分数时,最新提交的在第一位。

源码地址:https://github.com/Edenwds/springboot_study/tree/master/redis
参考资料:https://www.tutorialspoint.com/spring_boot/spring_boot_database_handling.htm
http://blog.didispace.com/springbootredis/

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值