redis学习1

一、概念复习

关系型数据库和非关系型数据库的区别
关系型数据库:数据之间有关联关系,数据存储在硬盘上,以表的形式存储数据。
非关系型数据库:redis,数据之间没有关联关系,以键值对的形式存储,存储在内存中。
与数据库交互比较耗时,查询一些不经常发生变化的数据时可以把其缓存在redis中,加快访问速度。
redis支持存储的值数据类型:
字符串,list,map,set,有序set
redis持久化方式:
RDB方式,在一定的间隔时间中检测key的变化,然后持久化数据。
jedis客户端操作redis

二、用jedis操作redis


public class Test1 {
    public static void main(String[] args) {
        //获取连接
        //Jedis jedis=new Jedis("localhost",6379);
        Jedis jedis = getJedisClien();//使用jedis连接池来获取jedis
        //设置字符串值
        jedis.set("username","zhangsan");
        //带过期时间设置值
        jedis.setex("password",10,"123");
        //获取字符串
        System.out.println(jedis.get("username"));

        //存储hash
        jedis.hset("student","name","zhangsan");
        jedis.hset("student","age","18");
        //获取map中的某个值
        System.out.println(jedis.hget("student","age"));
        //获取整个map
        Map<String, String> map = jedis.hgetAll("student");
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry.getKey()+":"+entry.getValue());
        }

        //存储列表
//        jedis.lpush("list","a");
//        jedis.lpush("list","b");
//        jedis.rpush("list","c");
        //list范围获取
        List<String> list = jedis.lrange("list", 0, -1);
        System.out.println(list);
        //list弹出
        String ele = jedis.lpop("list");
        System.out.println(ele);
        String ele2 = jedis.rpop("list");
        System.out.println(ele2);

        //set集合类型数据
        jedis.sadd("myset","java","php","c_sharp");
        jedis.srem("myset","java");//移除set集合中的某个元素
        Set<String> myset = jedis.smembers("myset");//获取set集合的全部元素
        System.out.println(myset);

        //有序set存储,根据给定的分数排序,分数越大越靠后
        jedis.zadd("mysortSet",1,"a");
        jedis.zadd("mysortSet",10,"c");
        jedis.zadd("mysortSet",100,"b");
        System.out.println(jedis.zrange("mysortSet",0,-1));

        //指定某个key的过期时间
        jedis.expire("student",15);
        jedis.close();//使用连接池获取到的连接,调用close方法就是把连接归还连接池
    }

    /**
     * 用jedis连接池来获取jedis对象
     * @return
     */
    public static Jedis getJedisClien(){
        JedisPoolConfig config=new JedisPoolConfig();
        config.setMaxTotal(10);
        config.setMaxIdle(10);
        JedisPool pool=new JedisPool(config,"localhost",6379);
        return pool.getResource();
    }
}

三、springboot整合redis

spring提供了一个RedisTemplate对象可以用来操作redis,在springboot中当然也可以使用redisTemplate

3.1 整合步骤

引入redis的起步依赖

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
</dependencies>

在配置文件中对redis和jedis的连接信息进行配置

spring:
  redis:
    host: localhost
    port: 6379
    jedis:
      pool:
        max-idle: 10
        max-active: 8
        max-wait: -1
        min-idle: 0

在需要操作redis的地方注入redisTemplate的对象,就可以使用

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RedisStudyApplication.class)
public class Test1 {

    @Autowired
    private RedisTemplate redisTemplate;//这个对象的泛型是<Object,Object>

    @Test
    public void test1(){
        //存储和获取字符串
        redisTemplate.boundValueOps("username").set("zhangsan");
        //使用泛型是<Object,Object>的模板对象,get方法返回的是Object
        Object value = redisTemplate.boundValueOps("username").get();
        System.out.println(value);

        //存储和获取map
        redisTemplate.boundHashOps("demoMap").put("key1","value1");
        Object value2 = redisTemplate.boundHashOps("demoMap").get("key1");
        System.out.println(value2);
    }
}

3.2 自动注入的redistemplate对象详解

springboot提供了redis的自动配置类来创建redisTemplate对象,配置类如下:


@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

可以看到其中提供了两个redisTemplate对象,一个泛型是RedisTemplate<Object, Object>,另一个的泛型是<String,String>。

使用这种方式注入的是泛型是<Object, Object>的redisTemplate对象

@Autowired
private RedisTemplate redisTemplate;

所以使用这个redisTemplate操作redis存储的key和value都是object类型的。

3.3 使用StringRedisTemplate

	/**
     * 测试 StringRedisTemplate来操作redis,序列化器使用的是 StringRedisSerializer
     */
    @Test
    public void test2(){
        //1.存储和获取字符串
        stringRedisTemplate.boundValueOps("accountName").set("lyy");
        //使用指定泛型的模板对象来操作redis,get方法返回的对象就是指定的泛型类型的
        String value = stringRedisTemplate.boundValueOps("accountName").get();
        System.out.println(value);

        //2.redis中存取hash类型的数据
        stringRedisTemplate.boundHashOps("stringMap").put("key1","value1");
        //通过redistemplate来获取hash类型的数据
        Object value2 = stringRedisTemplate.boundHashOps("stringMap").get("key1");
        System.out.println(value2);
    }

3.4序列化器

将一个对象存储到redis中,需要把一个对象序列化即保存这个对象的状态,再把序列化的结果保存到redis中。

同样的,从redis中把一个对象读取到内存中需要反序列化这个对象。

spring提供了几个序列化器

Jackson2JsonRedisSerializer
JdkSerializationRedisSerializer
OxmSerializer
StringRedisSerializer
GenericToStringRedisSerializer
GenericJackson2JsonRedisSerializer

当我们自动注入泛型是<Object,Object>的redisTemplate对象时,默认使用的是jdk自己的序列化器,所以序列化后存储到redis的结果会和实际的内容有出入,不管是key还是value都多了一些特殊内容。

当使用StringRedisTemplate时,序列化使用的是StringRedisSerializer这个序列化器,部分源码如下

public class StringRedisTemplate extends RedisTemplate<String, String> {

	/**
	 * Constructs a new <code>StringRedisTemplate</code> instance. {@link #setConnectionFactory(RedisConnectionFactory)}
	 * and {@link #afterPropertiesSet()} still need to be called.
	 */
	public StringRedisTemplate() {
		RedisSerializer<String> stringSerializer = new StringRedisSerializer();
		setKeySerializer(stringSerializer);
		setValueSerializer(stringSerializer);
		setHashKeySerializer(stringSerializer);
		setHashValueSerializer(stringSerializer);
	}

使用这个序列化器把字符串序列化后再存储到redis中,序列化的结果和实际的内容是一样的

3.5 自定义redisTemplate的泛型和序列化器

springboot自定配置的redisTemplate不满足使用要求时,可以自己在配置类中配置一个redisTemplate对象。

	/**
     * 自定义的redisTemplate对象
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> objectRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        //设置template的序列化器
        template.setKeySerializer(new StringRedisSerializer());//设置key的序列化器
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        //配置序列化器的属性
        ObjectMapper mapper=new ObjectMapper();
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //配置这个属性后从redis中再拿取到的就还是原来的对象,不配置这个属性存进去的是对象,获取到的会是一个map
        serializer.setObjectMapper(mapper);
        template.setValueSerializer(serializer);//设置value的序列化器
        template.setHashKeySerializer(serializer);
        template.setHashValueSerializer(serializer);
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

使用自定义的redisTemplate

	/**
     * 使用自定义的redisTemplate,key的序列化器使用StringRedisSerializer,value的序列化器使用Jackson2JsonRedisSerializer
     */
    @Test
    public void test3(){
        //1.存储和获取字符串
        myRedisTemplate.boundValueOps("demo3").set("demo3-value");
        Object demo3Value = myRedisTemplate.boundValueOps("demo3").get();
        System.out.println(demo3Value);
        //2.将一个map对象序列化成字符串然后存入redis中 redis中存储key-string类型
        Map<String,String> map=new HashMap<>();
        map.put("key1","value1");
        map.put("key2","value2");
        myRedisTemplate.boundValueOps("mapString").set(map);
        Object map1 = myRedisTemplate.boundValueOps("mapString").get();
        System.out.println(map1);

        //2.把一个自定义对象序列化成字符串存入redis reids中存储 key-string类型
        Student stu=new Student("1","zhangsan",18);
        myRedisTemplate.boundValueOps("student").set(stu);
        Object student = myRedisTemplate.boundValueOps("student").get();
        System.out.println(student);

        //3.给redis中存入hash类型的数据,hash中存储的是student对象(序列化成字符串),redis中存储的是key-hash类型
        Student st1=new Student("2","st1",19);
        Student st2=new Student("3","st2",19);
        myRedisTemplate.boundHashOps("studentMap").put("st1",st1);
        myRedisTemplate.boundHashOps("studentMap").put("st2",st2);
        Object obj = myRedisTemplate.boundHashOps("studentMap").get("st1");
        System.out.println(obj);
    }

注意:

在配置value的序列化器时使用了

		//配置序列化器的属性
        ObjectMapper mapper=new ObjectMapper();
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //配置这个属性后从redis中再拿取到的就还是原来的对象,不配置这个属性存进去的是对象,获取到的会是一个map
        serializer.setObjectMapper(mapper);

是因为配置了这个后对象序列化成字符并存储到redis时字符串中会包含它的来源对象的信息,这样反序列时就可以重新反序列化成原来的对象。

此序列化器序列化对象的字符串的信息如下:

"[\"com.lyy.entity.Student\",{\"id\":\"1\",\"userName\":\"zhangsan\",\"age\":18}]"

这个字符串中包含了原始类Student的全类名,所以反序列化时就可以根据这个把其还原成原来的对象。

四、总结

redis中可以存储五种数据类型:String,List,hash,set,sortSet

springboot操作redis使用的核心对象是redisTemplate

springboot的redis自动配置类配置了两个redisTemplate<Object,Object>,和StringRedisTemplate<String,String>

存储一个对象到redis中需要把对象序列化,spring提供了多个序列化器。

可以自己配置指定泛型的redisTemplate,并指定序列化器。