1.  连 接  redis 

        默认有三种方式连接redis.

第一种:jedis---传统的项目--ssm

第二种:lettuce:---->刚出现没有多久就被springboot整合进来。

第三种:springboot连接redis

1.1  jedis操作redis服务器

(1)引入jedis依赖

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

 (2)编写相关的代码

string字符串命令:

@Test

    //jedis连接
    public void test01(){
        //获取jedis对象
        Jedis jedis = new Jedis ("192.168.28.222",6379);

        //获取所有
        Set<String> keys = jedis.keys ("*");
        System.out.println (keys);

        //判断是否存在
        Boolean k1 = jedis.exists ("是否存在-----" + "k1");
        System.out.println (k1);


        //string类 型 测 试
        //存值
        String set = jedis.set ("k6", "wjj");
        System.out.println (set);

        //取值
        String k6 = jedis.get ("k6");
        System.out.println (k6);

        //删除

        Long k61 = jedis.del ("k6");
        System.out.println ("删除-----" + k61);

        //设置时间
        jedis.expire ("k2",60);


        //查看剩余时间
        Long k51 = jedis.ttl ("k2");
        System.out.println ("剩余" + k51);

        //测试是否存在
        Boolean k2 = jedis.exists ("k2");
        System.out.println ("是否存在" + k2);

        //存在不存,不存在直接存入
        Long k21 = jedis.setnx ("k6", "33");
        System.out.println ("k21------" + k21);



        //指定数值增加数值
        Long k63 = jedis.incrBy ("k6", 5);
        String k62 = jedis.get ("k6");
        System.out.println (k62);

        //指定数值减少

        Long k64 = jedis.decrBy ("k6", 6);
        String k55 = jedis.get ("k6");
        System.out.println (k55);



        jedis.close ();
    }

hash字段命令:

@Test
    public void test04(){
        //获取jedis对象
        Jedis jedis = new Jedis ("192.168.28.222",6379);

        //hash类型
        HashMap<String, String> hashMap = new HashMap<> ();
        hashMap.put ("name","wjjj");
        hashMap.put ("age","66");
        hashMap.put ("add","厕所");
        //存入数据
        Long k7 = jedis.hset ("k7----", hashMap);
        System.out.println (k7);

        //获取全部字段
        Map<String, String> k71 = jedis.hgetAll ("k7");
        System.out.println (k71);

        //获取所有字段
        Set<String> k72 = jedis.hkeys ("k7");
        System.out.println ("字段----" + k72);

        //获取所有值
        List<String> k73 = jedis.hvals ("k7");
        System.out.println ("值-----" + k73);

        //获取表中字段的数量
        Long k74 = jedis.hlen ("k7");
        System.out.println (k74);

        //获取所有给定字段的值
        List<String> hmget = jedis.hmget ("k7", "name", "age");
        System.out.println ("指定字段的值" + hmget);


        //只有在字段 field 不存在时,设置哈希表字段的值
        Long hsetnx = jedis.hsetnx ("k7", "sex", "男");
        System.out.println (hsetnx);



        //list队列
        Long lpush = jedis.lpush ("k8", "aa", "bb", "cc", "dd");

        List<String> k8 = jedis.lrange ("k8", 0, -1);
        System.out.println (k8);


        jedis.close ();
    }

list   列表命令:

@Test
    public void test05(){
        //获取jedis对象
        Jedis jedis = new Jedis ("192.168.28.222",6379);

        //list队列

        //添加数据
        Long lpush = jedis.lpush ("k8", "aa", "bb", "cc", "dd");

        //获取数据
        List<String> k8 = jedis.lrange ("k8", 0, -1);
        System.out.println ("元素----" + k8);

        //获取列表长度
        Long k81 = jedis.llen ("k8");
        System.out.println ("长度-----" + k81);

        //获取最后一个元素并移除
        String k82 = jedis.rpop ("k8");
        List<String> k9 = jedis.lrange ("k8", 0, -1);
        System.out.println ("元素剩余-----" + k9);


        //通过索引获取列表中的元素
        String k83 = jedis.lindex ("k8", 4);
        System.out.println ("第五个数据" + k83);
        

        jedis.close ();
    }

set列表命令:

@Test
    public void test06(){
        //获取jedis对象
        Jedis jedis = new Jedis ("192.168.28.222",6379);

        // set  集合

        //添加数据
        Long sadd = jedis.sadd ("k9", "aa", "bb", "cc", "dd", "ee");
        Long sadd1 = jedis.sadd ("k10", "aa", "bb", "cc", "gg", "ll");

        System.out.println ("添加数据" + sadd);

        //取数据
        Set<String> k9 = jedis.smembers ("k9");
        System.out.println ("取数据" + k9);

        //取出交集
        Set<String> sinter = jedis.sinter ("k9", "k10");
        System.out.println ("交集" + sinter);

        //返回给定所有集合的差集

        Set<String> sdiff = jedis.sdiff ("k9", "k10");
        System.out.println ("差集" + sdiff);

        

        jedis.close ();
    }

sort  set 有序集合命令:

@Test
    public void test07(){
        //获取jedis对象
        Jedis jedis = new Jedis ("192.168.28.222",6379);

        //  sort  set  集合

        //添加数据
        HashMap<String, Double> hashMap = new HashMap<> ();

        hashMap.put ("yw",99.0);
        hashMap.put ("sx",88.0);
        hashMap.put ("yy",65.0);
        hashMap.put ("ls",69.0);
        hashMap.put ("hx",85.0);

        Long k11 = jedis.zadd ("k11", hashMap);
        System.out.println (k11);

        //返回区间之内的
        Set<String> k111 = jedis.zrange ("k11", 0, -1);
        System.out.println ("返回" + k111);

        //返回有序集合中指定成员的排名,有序集成员按分数值递减
        Long zrevrank = jedis.zrevrank ("k11", "ls");
        System.out.println ("排名" + zrevrank);

        //获取有序集合的成员数

        Long k112 = jedis.zcard ("k11");
        System.out.println ("成员数" + k112);


        //返回有序集中指定区间内的成员,通过索引,分数从高到底
        Set<String> k113 = jedis.zrevrange ("k11", 0, -1);
        System.out.println ("分数" + k113);

        jedis.close ();
    }

1.2 .测试jedis使用和不使用连接池的效率

配置连接池

@Test
    public void test02(){
        //连接池的配置信息
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig ();
        jedisPoolConfig.setMaxTotal (10);//最大连接个数
        jedisPoolConfig.setMinIdle (2);//最小空闲时间
        jedisPoolConfig.setMaxIdle (10);//最大空闲时间
        jedisPoolConfig.setTestOnBorrow (true);//在连接对象时验证是否有联通性

        JedisPool jedisPool = new JedisPool (
                jedisPoolConfig,"192.168.28.222",6379);


        Jedis jedis = jedisPool.getResource ();
        //清空当前库
        jedis .flushDB ();

        String k5 = jedis.set ("k5", "55");
        String k51 = jedis.get ("k5");
        System.out.println (k51);

        jedis.close ();//归还连接池

    }

(1.)  不使用连接池

springcloud redis链接配置 spring连接redis_spring

(2.)使用连接池

springcloud redis链接配置 spring连接redis_java_02

结论:使用连接池 比没用要快很多

2. springboot 整合redis

        springboot在整合redis时提高两个模板类,StringRedisTemplate和RedisTemplate.以后对redis的操作都在该模板类中。StringRedisTemplate是RedisTemplate的子类。

2.1 导入依赖

<!--redis相关依赖-->

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

        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

2.2 修改配置文件

#redis的配置信息
spring.redis.host=192.168.28.222
spring.redis.port=6379

#最多获取数
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0

2.3 添加配置类

RedisTemplate

        它是StringRedisTemplate的父类,它类可以存储任意数据类型,但是任意类型必须序列化,默认采用的是jdk的序列化方式。jdk序列化方式阅读能力差,而且占用空间大. 我们在使用是一般需要人为指定序列化方式。

package com.dxh;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

/**
 * @program: redis
 * @author: ♥丁新华
 * @create: 2023-04-24 16:17
 **/
@Configuration
public class RedisConfig {
    //比如验证码
    @Bean //该方法的返回对象交于spring容器管理
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer ();
        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);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //key hashmap序列化
        template.setHashKeySerializer (redisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }
}

2.4 测  试 

(1 )关于key的方法

@SpringBootTest
class RedisApplicationTests {

    //因为springboot整合redis时会把StringRedisTemplate创建并交于spring容器管理
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Test
    void contextLoads() {


        //关于key的方法



        //获取所有key
        Set<String> keys = redisTemplate.keys ("*");
        System.out.println (keys);

        //判断key是否存在
        Boolean k1 = redisTemplate.hasKey ("k1");
        System.out.println (k1);

        //删除指定key
        Boolean k11 = redisTemplate.delete ("k1");
        System.out.println (k11);

        //设置过期时间
        Boolean k12 = redisTemplate.expire ("k1", 60, TimeUnit.MILLISECONDS);

        //获取还剩多少时间
        redisTemplate.getExpire ("k1");



    }

}

(2.) 关于字符串的操作

@SpringBootTest
class RedisApplicationTests3 {

    //因为springboot整合redis时会把StringRedisTemplate创建并交于spring容器管理
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Test
    void contextLoads() {


        //操作字符串

        //操作字符串===StringRedisTemplate会把对每一种数据的操作单独封装成一个类
        ValueOperations<String, String> s1 = redisTemplate.opsForValue ();

        //添加数据
        s1.set ("k1","33");

        s1.set ("k2","44");
        //获取数据  get
        String k12 = s1.get ("k1");
        System.out.println ("k1-----" + k12);

        //判断是否存入成功  setIfAbsent
        Boolean aBoolean = s1.setIfAbsent ("k2", "wjj");
        System.out.println ("是否----" + aBoolean);



        //增加指定时间  increment
        Long k3 = s1.increment ("k3", 20);
        System.out.println ("k3---" + k3);

        //减少指定时间  decrement
        Long k31 = s1.decrement ("k3", 10);
        System.out.println ("k3----" + k31);


        ArrayList<String> list = new ArrayList<> ();
        list.add ("k1");
        list.add ("k2");
        list.add ("k3");


        //获取多个  multiGet
        List<String> list1 = s1.multiGet (list);
        System.out.println ("多个----" + list1);


        //判断是否存在,
        Boolean k6 = s1.setIfPresent ("k6", "22");
        System.out.println ("成功是否----" + k6);

        //获取原值同时设置新值【getset】
        String andSet = s1.getAndSet ("k2", "ghj");
        System.out.println ("同步----" + andSet);

    }

}

3.   操作 hash 值

@SpringBootTest
class RedisApplicationTests4 {

    //因为springboot整合redis时会把StringRedisTemplate创建并交于spring容器管理
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Test
    void contextLoads() {



        // 操作hash方法
        HashOperations<String, Object, Object> s2 = redisTemplate.opsForHash ();
        // HashOperations<String, Object, Object> forHash = redisTemplate.opsForHash();

        // 向其中添加数据
        s2.put("k1","name","平家祥");
        s2.put("k1","age","16");
        s2.put("k1","sex","男");

        // 获取值  values
        List<Object> k1 = s2.values ("k1");
        System.out.println ("值---" + k1);

        //  获取变量中的键值对 entries
        Map<Object, Object> k11 = s2.entries ("k1");
        System.out.println ("键值对---" + k11);

        //通过hasKey(H key, Object hashKey)方法判断变量中是否存在map键
        Boolean k12 = s2.hasKey ("k1", "22");
        System.out.println ("通过hasKey(H key, Object hashKey)方法判断变量中是否存在map键---" + k12);

        //keys 获取变量中的键
        Set<Object> k13 = s2.keys ("k1");
        System.out.println ("变量中的键----" + k13);

        //获取长度
        Long k14 = s2.size ("k1");
        System.out.println ("长度----" + k14);

    }

}

4. 操作  list  

@SpringBootTest
class RedisApplicationTests5 {

    //因为springboot整合redis时会把StringRedisTemplate创建并交于spring容器管理
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Test
    void contextLoads() {


        //操作list集合方法
        ListOperations<String, String> s3 = redisTemplate.opsForList ();

        //左侧添加数据
        Long k1 = s3.leftPush ("k1", "22");
        System.out.println ("左侧添加---" + k1);

        //右侧添加数据
        Long k2 = s3.rightPush ("k2", "33");
        System.out.println ("左侧添加---" + k2);

        //获取元素
        String k11 = s3.index ("k1", 0);
        System.out.println ("元素----" + k11);

        //范围查找
        List<String> k12 = s3.range ("k1", 0, -1);
        System.out.println ("范围查找" + k12);

        //列表长度
        Long k13 = s3.size ("k1");
        System.out.println ("长度" + k13);
        
        

    }

}

5. set 集合

@SpringBootTest
class RedisApplicationTests6 {

    //因为springboot整合redis时会把StringRedisTemplate创建并交于spring容器管理
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Test
    void contextLoads() {


        //操作set集合方法
        SetOperations<String, String> s4 = redisTemplate.opsForSet ();

        //添加元素
        Long k1 = s4.add ("k1", "22", "33", "44", "55");
        Long k66 = s4.add ("k2", "22", "66", "44", "58");


        //移除
        Long k11 = s4.remove ("k1", "22");
        System.out.println ("移除----" + k11);


        //集合大小
        Long k12 = s4.size ("k1");
        System.out.println ("集合大小-----" + k12);

        //判断是否存在
        Boolean k13 = s4.isMember ("k1", "33");
        System.out.println ("是否存在----" + k13);

        //随机获取一个元素
        String k14 = s4.randomMember ("k1");
        System.out.println ("随机元素-----" + k14);

        //获取两个交集
        Set<String> intersect = s4.intersect ("k1", "k2");
        System.out.println ("交集---" + intersect);

    }

}

6. zset 有序集合

@SpringBootTest
class RedisApplicationTests7 {

    //因为springboot整合redis时会把StringRedisTemplate创建并交于spring容器管理
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Test
    void contextLoads() {


        //操作zSet有序集合方法
        ZSetOperations<String, String> s6 = redisTemplate.opsForZSet ();



        //添加元素
        s6.add ("k1","english",88.0);
        s6.add ("k1","math",66.0);
        s6.add ("k1","history",86.0);

        //获取元素

        Set<String> k1 = s6.range ("k1", 0, -1);
        System.out.println ("元素---" + k1);

        //获取指定范围
        Set<String> k11 = s6.range ("k1", 0, -1);
        System.out.println ("指定范围---" + k11);

        //指定元素增加指定值
        Double aDouble = s6.incrementScore ("k1", "math", 22.0);
        System.out.println ("增加---" + aDouble);

        //获取对应key和value的score
        Double score = s6.score ("k1", "english");
        System.out.println ("获取对应key和value的score" + score);



    }

}

3. springboot连接集群

3.1 配置文件

spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0


# 设置redis重定向的次数---根据主节点的个数
spring.redis.cluster.max-redirects=3

spring.redis.cluster.nodes=192.168.28.222:7001,192.168.28.222:7002,192.168.28.222:7003,\
  192.168.28.222:7004,192.168.28.222:7005,192.168.28.222:7006

3.2 连接多个端口

springcloud redis链接配置 spring连接redis_System_03

3.3 添加数据

@SpringBootTest
class RedisApplicationTests8 {

    //因为springboot整合redis时会把StringRedisTemplate创建并交于spring容器管理
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void contextLoads() {

        ValueOperations vs = redisTemplate.opsForValue ();

        vs.set ("k2","55");



    }

}

 

3.4 测试

springcloud redis链接配置 spring连接redis_redis_04

 

4. redis的应用场景

(1.) 缓存原理

springcloud redis链接配置 spring连接redis_redis_05

 

(2)缓存的作用:

用缓存,主要有两个用途:高性能、高并发。

高性能

假设这么个场景,你有个操作,一个请求过来,吭哧吭哧你各种乱七八糟操作 mysql,半天查出来一个结果,耗时 600ms。但是这个结果可能接下来几个小时都不会变了,或者变了也可以不用立即反馈给用户。那么此时咋办?

缓存啊,折腾 600ms 查出来的结果,扔缓存里,一个 key 对应一个 value,下次再有人查,别走 mysql 折腾 600ms 了,直接从缓存里,通过一个 key 查出来一个 value,2ms 搞定。性能提升 300 倍。

就是说对于一些需要复杂操作耗时查出来的结果,且确定后面不怎么变化,但是有很多读请求,那么直接将查询出来的结果放在缓存中,后面直接读缓存就好。

高并发

mysql 这么重的数据库,压根儿设计不是让你玩儿高并发的,虽然也可以玩儿,但是天然支持不好。mysql 单机支撑到 2000QPS 也开始容易报警了。

所以要是你有个系统,高峰期一秒钟过来的请求有 1万,那一个 mysql 单机绝对会死掉。你这个时候就只能上缓存,把很多数据放缓存,别放 mysql。缓存功能简单,说白了就是 key-value 式操作,单机支撑的并发量轻松一秒几万十几万,支撑高并发 so easy。单机承载并发量是 mysql 单机的几十倍。
 

(3)什么样的数据适合放入缓存

1.数据量不大

2.访问频率高

3.数据更改频率低

(4)如何使用缓存

1. 导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>redis-springboot01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>redis-springboot01</name>
    <description>redis-springboot01</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2. 配置文件

server.port=8888

spring.datasource.url=jdbc:mysql:///student
spring.datasource.password=root
spring.datasource.username=abc123
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

#redis的配置信息
spring.redis.host=192.168.28.222
spring.redis.port=6379
#最多获取数
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0

3. 编写mapper层

@Mapper
public interface StudentMapper extends BaseMapper<Student>{

}

4. 编写service层

public interface StudentService {

     /**
      * 根据ID查询
      * @param id
      * @return
      */
     Student findById (Integer id);
}

5. 服务层

@Service
public class StudentServiceimpl implements StudentService {

    @Resource
    private StudentMapper studentMapper;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public Student findById(Integer id) {
        ValueOperations<String, Object> forValue = redisTemplate.opsForValue();
        //1.查询缓存
        Object o = forValue.get("dept::" + id);
        //判断
        if(o!=null){ //证明缓存里边有
            //返回数据
            return (Student) o;
        }
        //调用方法
        Student student = studentMapper.selectById(id);
        //查询之后直接放入缓存
        if(student!=null){
            forValue.set("student::"+id,student);
        }

        return student;

    }



}

6. controller  层

/**
 * @program: redis-springboot01
 * @author: ♥丁新华
 * @create: 2023-04-25 11:20
 **/
@RestController
public class StudentController {
    @Resource
    private StudentService studentService;
    //restful风格
    @GetMapping("b/{id}")
    public Student findById(@PathVariable Integer id){
        return studentService.findById (id);

    }

}

7. 测  试

springcloud redis链接配置 spring连接redis_spring_06

 

控制台会查询数据库:

springcloud redis链接配置 spring连接redis_spring_07

 

因为使用了缓存机制,再次访问的话会走缓存,不会再去访问数据库,可以释放一定的压力,

springcloud redis链接配置 spring连接redis_redis_08