Redis

Posted xue_yun_xiang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis相关的知识,希望对你有一定的参考价值。

一、SpringBoot与Redis集成

1、引入依赖


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.1.5.RELEASE</version>
    </parent>

    <dependencies>

        <!--引入spring mvc 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--
            spirng boot 单元测试
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>


        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.1.0</version>
        </dependency>



        <!-- redis 配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>

    </dependencies>

2、创建配置文件

server.port=8080

# redis 单机配置
spring.redis.host=192.168.12.130
spring.redis.port=6379


#redis 集群配置
#spring.redis.cluster.nodes=192.168.12.130:7001,192.168.12.130:7002,192.168.12.130:7003,192.168.12.130:7004,192.168.12.130:7005,192.168.12.130:7006
#spring.activemq.close-timeout=5000

3、创建启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication 

    public static void main(String[] args) 

        SpringApplication.run(MyApplication.class,args);
    


4、单元测试类

重点:必须和启动类同一包

package com.qfedu;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

/**
 * 当前单元测试类 必须和启动类 在同一包下
 */

@RunWith(SpringRunner.class)
@SpringBootTest // 表明当前类是  springboot  单元测试类 必须和启动类 在同一包下
public class RedisTest 

    /**
     * StringRedisTemplate  所有存储数据都是string 字符串
     *    Template  模板  模板设计模式
     *              作用为使用方式 提供统一的 方法调用
     *
     */
    @Autowired // 从容器中获取stringRedisTemplate   key String  value 也是string (hash 除外)
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void  stringTest()

        // ValueOperations 就是 springboot 对应 redis 提供的 key String (key value) 类型的的操作类
        ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();


        valueOperations.set("name", "xiaoing ");
        String name = valueOperations.get("name");

        System.out.println("name = " + name);


    

    @Test
    public void hastTest()

        // 操作 值为 hash 接口
        HashOperations<String, Object, Object> hashOperations = stringRedisTemplate.opsForHash();

        // 设置数据
        hashOperations.put("user1", "name", "xiaoxue");


        String name = (String) hashOperations.get("user1", "name");

        System.out.println("name = " + name);

    

    /**
     * 测试 list
     */
    @Test
    public void listTest()

        ListOperations<String, String> listOperations = stringRedisTemplate.opsForList();


        listOperations.leftPush("list1", "a");
        listOperations.leftPush("list1", "b");
        listOperations.leftPush("list1", "c");


        List<String> list1 = listOperations.range("list1", 0, -1);
        System.out.println("list1 = " + list1);

    

    @Test
    public void boundValueTest()


        // 在获取时 直接绑定了key   省去了每次操作制定 key 的流程
        BoundValueOperations<String, String> valueOps = stringRedisTemplate.boundValueOps("name");

        valueOps.set("xiaowang");

        String name = valueOps.get();

        System.out.println("name = " + name);

    

    /**
     * 绑定 key  value Hash
     */
    @Test
    public void  boundHashTest()

        BoundHashOperations<String, Object, Object> hashOps = stringRedisTemplate.boundHashOps("user1");

        hashOps.put("name", "xiaocui");

        Object name = hashOps.get("name");

        System.out.println("name = " + name);
    



五、在springboot reids启动器中提供了两个模板类

StringRedisTemplate

public class StringRedisTemplate extends RedisTemplate<String, String> 
    public StringRedisTemplate() 
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    

RedisTemplate

 public void afterPropertiesSet() 
        super.afterPropertiesSet();
        boolean defaultUsed = false;
        if (this.defaultSerializer == null) 
            this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
        

        if (this.enableDefaultSerializer) 
            if (this.keySerializer == null) 
                this.keySerializer = this.defaultSerializer;
                defaultUsed = true;
            

            if (this.valueSerializer == null) 
                this.valueSerializer = this.defaultSerializer;
                defaultUsed = true;
            

            if (this.hashKeySerializer == null) 
                this.hashKeySerializer = this.defaultSerializer;
                defaultUsed = true;
            

            if (this.hashValueSerializer == null) 
                this.hashValueSerializer = this.defaultSerializer;
                defaultUsed = true;
            
        

        if (this.enableDefaultSerializer && defaultUsed) 
            Assert.notNull(this.defaultSerializer, "default serializer null and not all serializers initialized");
        

        if (this.scriptExecutor == null) 
            this.scriptExecutor = new DefaultScriptExecutor(this);
        

        this.initialized = true;
    

RedisTemplate 与 StringRedisTemplate区别

  • RedisTemplate:
    RedisTemplate是最基本的操作类,它默认的序列化方式是JdkSerializationRedisSerializer,在存值时,键值会被序列化为字节数组,可读性差,取值时也是一样,如果redis中存的值正常的字符串形式,取值时将返回null
  • StringRedisTemplate:
    StringRedisTemplate继承于 RedisTemplate<String, String>,默认的序列化方式是StringRedisSerializer,存值取值都是按照字符串的形式
 @Autowired// 序列化 jdk  序列化
    private RedisTemplate redisTemplate;

    @Test
    public void  redisTemplateTest1()


        ValueOperations valueOperations = redisTemplate.opsForValue();

        valueOperations.set("str1", "hallo world");
        System.out.println("str1:"+valueOperations.get("str1"));
    


解决RedistTemplate乱码

@Configuration
public class RedisConfig 

    
    @Bean("redisTemplate")
    public RedisTemplate redisTemplate(RedisConnectionFactory factory, Jackson2JsonRedisSerializer redisJsonSerializer) 
        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
        //redis连接工厂
        template.setConnectionFactory(factory);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //redis.key序列化器
        template.setKeySerializer(stringRedisSerializer);
        //redis.value序列化器
        template.setValueSerializer(redisJsonSerializer);
        //redis.hash.key序列化器
        template.setHashKeySerializer(stringRedisSerializer);
        //redis.hash.value序列化器
        template.setHashValueSerializer(redisJsonSerializer);
        //调用其他初始化逻辑
        template.afterPropertiesSet();
        //这里设置redis事务一致
        template.setEnableTransactionSupport(true);
        return template;
    

    /**
     * 配置redis Json序列化器
     *
     * @return
     */
    @Bean
    public Jackson2JsonRedisSerializer redisJsonSerializer() 
        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        return serializer;
    

二、Mybatis 使用Redis 作为二级缓存

单机 mybatis 开启二级缓存
1、在配置文件开启全局开关
2、在xml 注解 @CacheNamespace// 开启器二级缓存

mybatis 一级二级 缓存共同点
​在查询时 commit 产生缓存,只要发生增删改 清空缓存
mybatis 查询先去二级缓存,二级再去一级 ,一级没有去数据库差
mybatis-spring 应用中,每调用一次dao接口对应的方法,都会产生一个新的sqlSession,不存在一级缓存

1、mybatis 使用redis 作为二级缓存的优点

优点
1.提交查询效率
2.节约每个应用的jvm 内存空间 便于垃圾回收

2、在配置文件开启mybatis二级缓存

# 开启mybatis 二级缓存
mybatis.configuration.cache-enabled=true

3、创建 自定缓存类

package com.qfedu.cache;

import org.apache.ibatis.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 自定义 mybatis 二级缓存  底层使用 redis 存储
 *
 * 当前 cache 没有加入到容器
 *
 */

public class RedisCache implements Cache 

    private  String id;

    // 读写锁  可以多个人同时读,只要有一个人写 其他人都不能读
    private  ReadWriteLock readWriteLock = new ReentrantReadWriteLock();



    public RedisCache() 
    

    public RedisCache(String id) 
        System.out.println("命名空间-----id:"+id);
        this.id = id;
    

    // 获取的id 就是 当前mapper 的命名空间
    public String getId() 
        return id;
    


    /**
     * 向缓存中存放数据   key(类似sql) value (查询结果)
     * @param o
     * @param o1
     */
    public void putObject(Object key, Object value) 
        System.out.println("存储  key = " + key);
            getRedisTemplate().opsForValue().set(key.toString(), value, 10, TimeUnit.MINUTES);
    

    /**
     * 获取缓存是 通过 key
     * @param key
     * @return
     */
    public Object getObject(Object key) 
        System.out.println("获取key 对应的值  key = " + key);

        return getRedisTemplate().opsForValue().get(key.toString());
    

    /**
     * 清除key 对应的缓存
     * @param key
     * @return
     */
    public Object removeObject(Object key) 
        System.out.println("删除  key = " + key);
        return getRedisTemplate().delete(key.toString());
    

    /**
     * 清空当前命名空间二级缓存
     *          就是删除所有 对应namespace(id) 的 key
     */
    public void clear() 
        // 获取到所有包含 namespace(id) 的 key
        Set<String> keys = getRedisTemplate().keys("*" + id + "*");

        for (String key : keys) 
            System.out.println("遍历清空所有的namespace 下 的key = " + key);
            getRedisTemplate().delete(key);
        

    

    /**
     * 获取当前命名空间对应的条数
     * @return
     */
    public int getSize() 

        System.out.println("获取当前命名空间下 的条数");
        // 获取到所有包含 namespace(id) 的 key
        Set<String> keys = getRedisTemplate().keys("*" + id + "*");
        return keys.size();
    

    public ReadWriteLock getReadWriteLock() 
        return readWriteLock;
    


    /**
     * 获取容器中的 redisTemplate
     * @return
     */
    public  RedisTemplate getRedisTemplate()

        // 获取到容器
        ApplicationContext applicationContext = ApplicationHolder.getApplicationContext();

        return (RedisTemplate) applicationContext.getBean("redisTemplate");
    


package com.qfedu.cache;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 *
 * ApplicationContextAware  作用 就是 在生成 对应的实例是调用,将容器ApplicationContext 传进来
 */
@Component// 将当前类加入到容器中  ,如果当前类实现ApplicationContextAware 还会在创建bean调用改接口对应的setApplicationContext
public class ApplicationHolder implements ApplicationContextAware 

    private static ApplicationContext applicationContext;

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException 
        // 持有上下文对象
        this.applicationContext  = applicationContext;
    


    /**
     * 获取核心容器
     * @return
     */
    public static ApplicationContext getApplicationContext() 
        return applicationContext;
    

4、在xml 中使用自定义缓存

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qfedu.dao.StudentDao">

    <!--  开启二级缓存

        type="" mybatis  会使用 用户自定的二级缓存 存储 如果没有配置就是用 jvm

        mybatis 创建 RedisCache ,没有加入到容器中
    -->
    <cache type="com.qfedu.cache.RedisCache" eviction="LRU" size="1024" flushInterval="60"></cache>

    <!--
            在springboot中使用别名有 警告 误报没有影响
            可以使用全限定名解决  com.qfedu.entity.Student

    -->
    <select id="findAllStudent" resultType="com.qfedu.entity.Student">
        select * from student_tb
    </select>


    <update id="updateStudent">
        update student_tb  set name = #name,age =  #age,sex = #sex,height=#以上是关于Redis的主要内容,如果未能解决你的问题,请参考以下文章

8. Redis 持久化对生产环境的灾难恢复的意义

Redis

redis详解

Redis专题-Redis的持久化策略

Redis 事务支持回滚吗?

大厂面试06期谈一谈你对Redis持久化的理解?