Spring Boot学习笔记(十三)Spring Boot Redis 入门、详解、概念、实战

Spring Boot Redis 入门、详解、概念、实战

  • Redis介绍
    • Redis 是一个速度非常快的非关系数据库(non-relational database),它可以存储键(key)与 5 种不同类型的值(value)之间的映射(mapping)可以将存储在内存的键值对数据持久化到硬盘。
    • 可以使用复制特性来扩展读性能,还可以使用客户端分片来扩展写性能。
    • 为了满足高性能,Redis 采用内存(in-memory)数据集(dataset)。根据使用场景,可以通过每隔一段时间转储数据集到磁盘,或者追加每条命令到日志来持久化。如果只需要一个功能丰富,网络化的内存缓存,持久化也可以被禁用。
  • Redis数据模型
    • Redis 数据模型不仅与关系数据库管理系统(RDBMS)不同,也不同于任何简单的 NoSQL 键-值数据存储。
    • Redis 数据类型类似于编程语言的基础数据类型,每个数据类型都支持适用于其类型的操作。包括:
      string(字符串)
      hash(哈希)
      list(列表)
      set(集合)
      zset(sorted set:有序集合)
  • Redis优势
    • 速度:性能极高,它每秒可执行约 100,000 个 set 以及约 100,000 个 get 操作。
    • 对富数据类型的支持:丰富的数据类型,Redis 对大多数开发人员已知道的大多数据类型提供了原生支持。
    • 操作的原子性: 因为所有 Redis 操作都是原子性的,所以多个客户端会并发地访问一个 Redis 服务器,获取相同的更新值。
    • 通用性:Redis 是一个多效工具,有非常多的应用场景,包括缓存、消息队列(Redis 原生支持 发布/订阅)、短期应用程序数据(如 Web 会话、Web 页面命中计数)等。
  • 新建项目之前 : Win 10 环境下安装 Redis

  • 第一种方法:访问 https://start.spring.io/ , 点击下方Generate the Project,生成压缩包之后用 IDEA 打开在这里插入图片描述

  • 第二种方法新建 Spring Boot Redis项目:
    在这里插入图片描述

  • 选择Spring Initializer初始化项目在这里插入图片描述

  • 项目名为redisdemo,包名为wen
    在这里插入图片描述

  • 勾选web选项在这里插入图片描述

  • 勾选NoSQL – redis 选项,NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库
    在这里插入图片描述

  • 点击finish,构建工程
    在这里插入图片描述

  • 项目结构
    在这里插入图片描述

  • pom.xml

    • Spring Boot 提供了对 Redis 集成的组件包: spring-boot-starter-data-redis ,spring-boot-starter-data-redis 依赖于 spring-data-redis 和 jedis
    • Jedis:Jedis 是 Redis 的 Java 版客户端实现,也是官方推荐的 Java 版客户端。它封装了对 Redis 的各种操作,并且支持事务、管道及有 Jedis 自身实现的分布式
    • Spring Data:Spring Data 是 Spring 框架中的一个主要项目,目的是为了简化构建基于Spring 框架应用的数据访问,包括非关系数据库、Map-Reduce 框架、云数据服务等,另外也包含对关系数据库的访问支持
    • Spring Data Redis :Spring Data Redis 是 Spring Data 项目中的一个主要模块,实现了对 Jedis 客户端 API 的高度封装,使对 Redis 的操作更加便捷
    • 关系:Jedis – Spring Data Redis – Spring Data – spring-boot-starter-data-redis
    <?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.1.8.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>wen</groupId>
        <artifactId>redisdemo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>redisdemo</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.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.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
    
  • application.properties:

    • Spring Boot 默认继承了连接池
    spring.redis.database=1
    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    # redis 连接池设置
    spring.redis.jedis.pool.max-idle=8
    spring.redis.jedis.pool.min-idle=0
    spring.redis.jedis.pool.max-active=8
    spring.redis.jedis.pool.max-wait=-1
    spring.redis.timeout=60000
    
  • logback.xml 配置文件

    <configuration>
    
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%d %p (%file:%line\)- %m%n</pattern>
                <charset>GBK</charset>
            </encoder>
        </appender>
    
        <appender name="baselog"
                  class="ch.qos.logback.core.rolling.RollingFileAppender">
            <File>log/base.log</File>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>log/base.log.%d.%i</fileNamePattern>
                <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                    <maxFileSize>64 MB</maxFileSize>
                </timeBasedFileNamingAndTriggeringPolicy>
            </rollingPolicy>
            <encoder>
                <pattern>
                    %d %p (%file:%line\)- %m%n
                </pattern>
                <charset>UTF-8</charset>
            </encoder>
        </appender>
    
        <root level="info">
            <appender-ref ref="STDOUT"/>
        </root>
    
        <logger name="com.example" level="DEBUG">
            <appender-ref ref="baselog"/>
        </logger>
    
    </configuration>
    
  • TestController.java 测试连接

    • 在测试testVar方法时,网址输入“http://localhost:8080/test/Stephanie” 代表传给controller类的参数是Stephanie,所以返回了网址给定的变量名
    package wen.redisdemo.web;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    
    @RestController
    public class TestController {
    
        protected static Logger logger = LoggerFactory.getLogger(TestController.class);
    
        @RequestMapping("/")
        public String test() {
            logger.debug("测试连接");
            return "连接成功!";
        }
    
        @RequestMapping("/test/{var}")
        public String testVar(@PathVariable String var) {
            logger.debug("var={}", var);
            return "test " + var;
        }
    
    }
    
    
  • 开启项目后(RedisdemoApplication)测试结果
    在这里插入图片描述
    在这里插入图片描述

  • 接下来测试使用RedisTemplate, 由于Redis本质是一种缓存,所以它并不需要被写入数据库

  • 实体类 User.java

    package wen.redisdemo.entity;
    
    import java.io.Serializable;
    
    public class User implements Serializable {
    
        private Long id;
    
        private String username;
    
        private String password;
    
        public User() {
            super();
        }
    
        public User(Long id, String username, String password) {
            this.id = id;
            this.username = username;
            this.password = password;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String toString() {
            return "username: " + this.getUsername() + "\n" +
                    "password: " + this.getPassword();
        }
    }
    
    
  • 接口类 UserMapper.java

    • 在接口中注入RedisTemplate:RedisTemplate是一个储存键值类型数据的接口,但是具体实现储存的形式主要有五种,opsForValue、opsForList、opsForSet、opsForHash、opsForZSet
    • 如下文的 opsForValue(),这个方法会返回一个默认的操作类 DefaultValueOperations, 它提供了所有Redis字符串类型的操作api。比如set,get,incr等等。使用这些方法可以直接存储任意的java类型,而不需要将存储的东西序列化以及反序列化
    • ListOperations, SetOperations, ZSetOperations除了提供的操作API不一样以外,其他的调用方法与DefaultValueOperations一致
    package wen.redisdemo.mapper;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.ValueOperations;
    import org.springframework.stereotype.Repository;
    import wen.redisdemo.entity.User;
    
    @Repository
    public class UserMapper {
    
        @Autowired
        RedisTemplate redisTemplate;
    
        public void saveUser(User user) {
            ValueOperations<Long, User> valueOperations = redisTemplate.opsForValue();
            valueOperations.set(user.getId(), user);
            System.out.println(valueOperations.get("1"));
        }
    
        public User getUser(Long id) {
            ValueOperations<Long, User> valueOperations = redisTemplate.opsForValue();
            return valueOperations.get(id);
        }
    }
    
    
  • RedisController.java

    • /setUser :浏览器链接中输入user属性传给RedisController,之后调用UserMapper中的saveUser利用上文中的ValueOperations进行缓存存储
    • /getUser :参数只有id,即根据id来查询user信息,调用方法和上一点一样
    package wen.redisdemo.web;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import wen.redisdemo.entity.User;
    import wen.redisdemo.mapper.UserMapper;
    
    @RestController
    public class RedisController {
    
        protected static Logger logger = LoggerFactory.getLogger(RedisController.class);
    
        @Autowired
        UserMapper userMapper;
    
        @RequestMapping("/setUser")
        public void setUser(Long id, String username, String password) {
            logger.debug("setUser:id={}, username={}, password={}", id, username, password);
            User user = new User(id, username, password);
            userMapper.saveUser(user);
        }
    
        @RequestMapping("/getUser")
        public String getUser(Long id) {
            return "username:" + userMapper.getUser(id).getUsername() + "\n" +
                    "password: " + userMapper.getUser(id).getPassword();
        }
    
    }
    
    
  • 浏览器输入:http://localhost:8080/setUser?id=1&&username=Stephanie&&password
    =123,页面空白,代表已经存入缓存
    在这里插入图片描述

  • 浏览器输入 http://localhost:8080/getUser?id=1,可以获得对应的user信息
    在这里插入图片描述

  • Redis 支持多种数据类型,实体、哈希、列表、集合、有序集合,接下来就是各种类型的操作。

  • RedisService.java

    package wen.redisdemo.service;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.ValueOperations;
    import org.springframework.stereotype.Service;
    
    import java.io.Serializable;
    
    @Service
    public class RedisService {
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        protected static Logger logger = LoggerFactory.getLogger(RedisService.class);
    
        public void set(final String key, Object value) {
            try {
                ValueOperations<Serializable, Object> valueOperations = redisTemplate.opsForValue();
                valueOperations.set(key, value);
            } catch (Exception e) {
                logger.error("set error: key {}, value {}", key, value);
            }
        }
    
        public Object get(final String key) {
            ValueOperations<Serializable, Object> valueOperations = redisTemplate.opsForValue();
            return valueOperations.get(key);
        }
    
    }
    
    
  • RedisDemoApplicationTests.java

    package wen.redisdemo;
    
    import org.junit.Assert;
    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 wen.redisdemo.entity.User;
    import wen.redisdemo.service.RedisService;
    
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class RedisDemoApplicationTests {
    
        @Autowired
        RedisTemplate redisTemplate;
    
        @Autowired
        RedisService redisService;
    
        @Test
        public void contextLoads() {
        }
    
        /**
         * 取出缓存中的对象
         */
        @Test
        public void testObj() {
            User user = new User(1L, "I", "smile");
            ValueOperations<Long, User> valueOperations = redisTemplate.opsForValue();
            valueOperations.set(1L, user);
            User u = valueOperations.get(1L);
            System.out.println(u.toString());
        }
    
        /**
         * 超时失效测试
         */
        @Test
        public void testExpire() throws InterruptedException {
            User user = new User(2L, "I", "smile");
            ValueOperations<Long, User> valueOperations = redisTemplate.opsForValue();
            valueOperations.set(2L, user, 100, TimeUnit.MILLISECONDS);
            Thread.sleep(1000);
            boolean exist_flag = redisTemplate.hasKey(2L);
            if (exist_flag) {
                System.out.println("EXIST");
            } else {
                System.out.println("NOT EXIST");
            }
        }
    
        /**
         * 删除数据
         */
        @Test
        public void testDelete() {
            ValueOperations<Long, String> valueOperations = redisTemplate.opsForValue();
            User user = new User(3L, "she", "smiles");
            redisTemplate.opsForValue().set(3L, user);
            redisTemplate.delete(3L);
            boolean exist_flag = redisTemplate.hasKey(3L);
            if (exist_flag) {
                System.out.println("EXIST");
            } else {
                System.out.println("NOT EXIST");
            }
        }
    
        /**
         * 哈希表测试
         */
        @Test
        public void testHash() {
            HashOperations<Long, User, String> hash = redisTemplate.opsForHash();
            User user = new User(4L, "he", "haha");
            hash.put(4L, user, "lala");
            String value = (String) hash.get(4L, user);
            System.out.println(" 4L " + user.toString() + " : " + value);
        }
    
        /**
         * 队列
         */
        @Test
        public void testList() {
            ListOperations<String, String> list = redisTemplate.opsForList();
            list.leftPush("listKey", "first to get in");
            list.leftPush("listKey", "second to get in");
            list.leftPush("listKey", "third to get in");
            String value = (String) list.leftPop("listKey");
            System.out.println("list value : " + value.toString());
    
            List<String> values = list.range("listKey", 0, 2);
            for (String v : values) {
                System.out.println("list range : " + v);
            }
        }
    
        /**
         * set
         */
        @Test
        public void testSet() {
            String key = "set";
            SetOperations<String, String> set = redisTemplate.opsForSet();
            set.add(key, "Hi");
            set.add(key, "My");
            set.add(key, "Stephanie");
            set.add(key, "Stephanie");
            Set<String> values = set.members(key);
            for (String v : values) {
                System.out.println("set value : " + v);
            }
        }
    
        /**
         * 测试difference
         */
        @Test
        public void TestSetDifference() {
            SetOperations<String, String> set = redisTemplate.opsForSet();
            String key1 = "setOne";
            String key2 = "setTwo";
            set.add(key1, "a");
            set.add(key1, "b");
            set.add(key1, "c");
            set.add(key1, "c");
            set.add(key2, "a");
            set.add(key2, "d");
            Set<String> differ = set.difference(key1, key2);
            for (String v : differ) {
                System.out.println("difference from key1 to key2 : " + v);
            }
        }
    
        /**
         * 测试 unions
         */
        @Test
        public void TestUnions() {
            SetOperations<String, String> set = redisTemplate.opsForSet();
            String key3 = "setThree";
            String key4 = "setFour";
            set.add(key3, "hi");
            set.add(key3, "my");
            set.add(key3, "love");
            set.add(key4, "Here");
            set.add(key4, "I");
            set.add(key4, "am");
            Set<String> unions = set.union(key3, key4);
            for (String v : unions) {
                System.out.println("unions value : " + v);
            }
        }
    
        /**
         * 测试 ZSet
         */
        @Test
        public void testZSet() {
            String key = "zSetKey";
            redisTemplate.delete(key);
            ZSetOperations<String, String> zSet = redisTemplate.opsForZSet();
            zSet.add(key, "hi", 1);
            zSet.add(key, "how", 3);
            zSet.add(key, "are", 2);
            zSet.add(key, "you", 4);
    
            Set<String> zSetList = zSet.range(key, 0, 3);
            for (String v : zSetList) {
                System.out.println("zSet value : " + v);
            }
    
            Set<String> zSetTwo = zSet.rangeByScore(key, 0, 3);
            for (String v : zSetTwo) {
                System.out.println("zSetTwo value : " + v);
            }
        }
    
        @Test
        public void testString() throws Exception {
            redisService.set("Stephanie", "Went");
            Assert.assertEquals("Went", redisService.get("Stephanie"));
        }
    
    }
    
    
  • Redis 对 Pojo 的支持,新建一个 User 对象,放到缓存中,再取出来

    /**
     * 取出缓存中的对象
     */
    @Test
    public void testObj() {
        User user = new User(1L, "I", "smile");
        ValueOperations<Long, User> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(1L, user);
        User u = valueOperations.get(1L);
        System.out.println(u.toString());
    }
    

    在这里插入图片描述

  • 超时失效:Redis 在存入每一个数据的时候都可以设置超时时间,过了这个时间就会自动删除数据。这种特性非常适合我们对阶段数据的缓存。

  • 新建一个 User 对象,存入 Redis 的同时设置 100 毫秒后失效:valueOperations.set(2L, user, 100, TimeUnit.MILLISECONDS);设置一个线程暂停 1000 毫秒之后 Thread.sleep(1000),判断数据是否存在并打印结果。

    /**
     * 超时失效测试
     */
    @Test
    public void testExpire() throws InterruptedException {
        User user = new User(2L, "I", "smile");
        ValueOperations<Long, User> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(2L, user, 100, TimeUnit.MILLISECONDS);
        Thread.sleep(1000);
        boolean exist_flag = redisTemplate.hasKey(2L);
        if (exist_flag) {
            System.out.println("EXIST");
        } else {
            System.out.println("NOT EXIST");
        }
    }
    

    在这里插入图片描述

  • 删除数据,有些情况下需要对过期的缓存进行删除,我们来对 Redis 删除操作进行测试。首先 新建一个 User 对象,紧接着删除此 key 的值,再进行判断

        /**
         * 删除数据
         */
        @Test
        public void testDelete() {
            ValueOperations<Long, String> valueOperations = redisTemplate.opsForValue();
            User user = new User(3L, "she", "smiles");
            redisTemplate.opsForValue().set(3L, user);
            redisTemplate.delete(3L);
            boolean exist_flag = redisTemplate.hasKey(3L);
            if (exist_flag) {
                System.out.println("EXIST");
            } else {
                System.out.println("NOT EXIST");
            }
        }
    

    在这里插入图片描述

  • hash(哈希):一般我们存储一个键,就会使用 get/set 去存储,实际上这并不是很好的做法。Redis 存储一个 key会有一个最小内存,不管存的这个键多小,都不会低于这个内存,所以合理的使⽤用 Hash 可以帮我们节省很多内存。

  • hash set 就在哈希表 key 中的域(field)的值设为 value。如果 key 不存在,一个新的哈希表被创建并进行hash.set 操作。如果域(field)已经存在于哈希表中,旧值将被覆盖

        /**
         * 哈希表测试
         */
        @Test
        public void testHash() {
            HashOperations<Long, User, String> hash = redisTemplate.opsForHash();
            User user = new User(4L, "he", "haha");
            hash.put(4L, user, "lala");
            String value = (String) hash.get(4L, user);
            System.out.println(" 4L " + user.toString() + " : " + value);
        }
    

    在这里插入图片描述

  • List:Redis list 的应用场景非常多,也是 Redis 最重要的数据结构之一。

  • List典型的应用场景就是消息队列,可以利用 list 的 PUSH 操作,将任务存在 list 中,然后工作线程再用 POP 操作将任务取出执行。

  • 或者通过方法 range 读取队列的一部分。接着上面的例子使用 range 来读取

        /**
         * 队列
         */
        @Test
        public void testList() {
            ListOperations<String, String> list = redisTemplate.opsForList();
            list.leftPush("listKey", "first to get in");
            list.leftPush("listKey", "second to get in");
            list.leftPush("listKey", "third to get in");
            String value = (String) list.leftPop("listKey");
            System.out.println("list value : " + value.toString());
    
            List<String> values = list.range("listKey", 0, 2);
            for (String v : values) {
                System.out.println("list range : " + v);
            }
        }
    

    在这里插入图片描述

  • Redis set 对外提供的功能与 list 类似是一个列表的功能,特殊之处在于 set 是可以自动排重

  • set 还提供了判断某个成员是否在一个set 集合内的重要接口,这个也是 list 所不能提供的

  • Redis 为也集合提供了求交集、并集、差集等操作

        /**
         * set
         */
        @Test
        public void testSet() {
            String key = "set";
            SetOperations<String, String> set = redisTemplate.opsForSet();
            set.add(key, "Hi");
            set.add(key, "My");
            set.add(key, "Stephanie");
            set.add(key, "Stephanie");
            Set<String> values = set.members(key);
            for (String v : values) {
                System.out.println("set value : " + v);
            }
        }
    

    在这里插入图片描述

  • 根据下面这个例子可以看出,difference() 函数会把 key1 中不同于 key2 的数据对比出来、

        /**
         * 测试difference
         */
        @Test
        public void TestSetDifference() {
            SetOperations<String, String> set = redisTemplate.opsForSet();
            String key1 = "setOne";
            String key2 = "setTwo";
            set.add(key1, "a");
            set.add(key1, "b");
            set.add(key1, "c");
            set.add(key1, "c");
            set.add(key2, "a");
            set.add(key2, "d");
            Set<String> differ = set.difference(key1, key2);
            for (String v : differ) {
                System.out.println("difference from key1 to key2 : " + v);
            }
        }
    
    

    在这里插入图片描述

  • 根据下面例子可发现,unions 会取两个集合key3、key4的合集

  • set 的内部实现是一个 value 永远为 null 的 HashMap,实际就是通过计算 hash 的方式来快速排重,这也是 set 能提供判断一个成员是否在集合内的原因

        /**
         * 测试 unions
         */
        @Test
        public void TestUnions() {
            SetOperations<String, String> set = redisTemplate.opsForSet();
            String key3 = "setThree";
            String key4 = "setFour";
            set.add(key3, "hi");
            set.add(key3, "my");
            set.add(key3, "love");
            set.add(key4, "Here");
            set.add(key4, "I");
            set.add(key4, "am");
            Set<String> unions = set.union(key3, key4);
            for (String v : unions) {
                System.out.println("unions value : " + v);
            }
        }
    
    

    在这里插入图片描述

  • ZSet : Redis sorted set 的使用场景与 set 类似,区别是 set 不是自动有序的,而 sorted set 可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序,即自动排序。

  • 在使用 Zset 的时候需要额外的输入一个参数 score,Zset 会自动根据 Score 的值对集合进行排序,我们可以利用这个特性来做具有权重的队列,比如普通消息的 score 为1,重要消息的 score 为2,然后工作线程可以选择按 score 的倒序来获取工作任务

  • 通过下面的例子发现插入到 Zset 的数据会自动根据 score 进行排序,根据这个特性我们可以做优先队列等各种常见的场景。另外 Redis 还提供了 rangeByScore 方法,可以只获取 score 范围内排序后的数据。

  • Redis sorted set 的内部使用 HashMap 和跳跃表(SkipList)来保证数据的存储和有序,HashMap 里放的是成员到 score 的映射,而跳跃表里存放的是所有的成员,排序依据是 HashMap 里存的 score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单

        /**
         * 测试 ZSet
         */
        @Test
        public void testZSet() {
            String key = "zSetKey";
            redisTemplate.delete(key);
            ZSetOperations<String, String> zSet = redisTemplate.opsForZSet();
            zSet.add(key, "hi", 1);
            zSet.add(key, "how", 3);
            zSet.add(key, "are", 2);
            zSet.add(key, "you", 4);
    
            Set<String> zSetList = zSet.range(key, 0, 3);
            for (String v : zSetList) {
                System.out.println("zSet value : " + v);
            }
    
            Set<String> zSetTwo = zSet.rangeByScore(key, 0, 3);
            for (String v : zSetTwo) {
                System.out.println("zSetTwo value : " + v);
            }
        }
    

    在这里插入图片描述

  • 封装:在实际的使用过程中,一般都会对业务进行简单的包装,最后提供出来对外使用,例如:首先定义一个 RedisService 服务,将 redisTemplate 注入到类中

  • 在其他服务使用的时候将 redisService 注入其中,调用对应的方法操作 Redis,这样会具层次感在这里插入图片描述

        @Test
        public void testString() throws Exception {
            redisService.set("Stephanie", "Went");
            Assert.assertEquals("Went", redisService.get("Stephanie"));
        }
    
  • 总结

  • Redis 是一款非常优秀的高性能缓存中间件

  • Spring Boot 对 Redis 的操作提供了很多的支持

  • Redis 拥有丰富的数据类型,方便在不同的业务场景中使用,特别是提供了很多内置的高效集合操作

  • 源代码 :https://github.com/StephaineWYT/redisdemo

  • 下一篇:Spring Boot学习笔记(十四)Spring Boot Redis 数据缓存实例

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值