SpringBoot2.x整合Redis

在现今的互联网应用中, NoSQL 已经广为应用 , 在互联网中起到加速系统的作用。有两种 NoSQL 使用尤为广泛,那就是 Redis 和 MongoDB。本篇幅将对 Redis 和 Spring Boot 的整合做一个总结,而Spring Boot整合Redis 是通过Spring Data中的Redis模块完成的,所以再此之前还需对此模块有一个基础的认识。

Spring Boot整合Redis

引入依赖

pom文件核心配置【web启动器已经在本工程的父工程中导入】

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <!--不依赖Redis的客户端lettuce-->
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!--引入Redis的客户端驱动jedis-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
整合的配置文件
spring:
  cache:
    type: redis
  redis:
    database: 0
    host: localhost
    port: 6379
    password:
    jedis:
      pool:
        #        连接池中最大连接数
        max-active: 8
        #        连接池中最大阻塞等待时间
        max-wait: -1
        #        连接池中最大空闲连接数
        max-idle: 5
        #        连接池中最小空闲连接数
        min-idle: 0
    #        连接超时时间
    timeout: 10000
  application:
    name: boot2.x-data-redis
server:
  servlet:
    context-path: /boot2.x-data-redis
  port: 8379
整合配置如下
package top.chenfu;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;

import javax.annotation.PostConstruct;

/**
 * @Author: [email protected]
 * @Description:
 * @Date: 2019/9/6 11:22
 */
@SpringBootApplication
public class RedisApplication {

    @Autowired
    private RedisTemplate redisTemplate;

    public static void main(String[] args) {
        SpringApplication.run(RedisApplication.class, args);
    }

    /**
     * 自定义初始化方法
     */
    @PostConstruct
    public void init() {
        initRedisTemplate();
    }

    /**
     * 设置redisTemplate的序列化器
     */
    private void initRedisTemplate() {
        RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
    }
       
}
操作 Redis 数据类型

下面来增加实践案例。这里主要演示常用 Redis 数据类型(如字符串、散列、列表、集合和有序集合)的操作。因为在大部分的场景下, 并不需要很复杂地操作 Redis,而仅仅是很简单 地使用而己,也就是只需要操作一次 Redis, 这个时候使用 RedisTemplate 的操作还是比较多的。 如果需要多次执行 Redis 命令,可以选择使用 SessionCallback 或者 RedisCallback 接口 。

操作 Redis 字符串
@GetMapping("/str/{number}")
public Object testStr(@PathVariable(required = false) Number number) {
    redisTemplate.opsForValue().set("s1", "1");
    stringRedisTemplate.opsForValue().set("s2", "0");
    if (number instanceof Number) {
        redisTemplate.opsForValue().increment("s1", number.doubleValue());
        stringRedisTemplate.opsForValue().increment("s2", number.doubleValue());
    }
    Jedis jedis = (Jedis) redisTemplate.getConnectionFactory().getConnection().getNativeConnection();
//        获取原生的redis对象,进行redisTemplate没有的操作【decr】
    redisTemplate.opsForValue().increment("s4", 2);
    jedis.decr("s4");
    return new ResponseEntity<>(HttpStatus.OK);
}
操作散列数据
@GetMapping("/hash")
public Object testHash() {
    HashMap<String, String> hashMap = new HashMap<>();
    hashMap.put("f1", "v1");
    hashMap.put("f2", "v2");
    redisTemplate.opsForHash().putAll("h1", hashMap);
    stringRedisTemplate.opsForHash().putAll("sh1", hashMap);
    stringRedisTemplate.opsForHash().put("sh2", "ff1", "sh2-v1");
    stringRedisTemplate.opsForHash().put("sh2", "ff2", "sh2-v2");
    BoundHashOperations sh2 = redisTemplate.boundHashOps("sh2");
    sh2.delete("ff1");
    sh2.putAll(hashMap);
    sh2.put("ff3", "sh2-v3");
    return new ResponseEntity<>(HttpStatus.OK);
}
操作列表List(链表)

列表也是常用的数据类型。在 Redis 中列表是一种链表结构,这就意味着查询性能不高, 而增删节点的性能高, 这是它的特性。在 Redis 中存在从左到右或者从右到左的操作。

@GetMapping("/list")
public Object testList() {
//        V2,V1
    redisTemplate.opsForList().leftPushAll("list1", "v1", "v2");
    redisTemplate.opsForList().leftPushAll("list2", "v1", "v2");
//        V2,V1,V3,V4
    stringRedisTemplate.opsForList().rightPushAll("list1", "v3", "v4");
    BoundListOperations list1 = stringRedisTemplate.boundListOps("list1");
    System.out.println("rightPop=" + list1.rightPop());
//        获取定位元素,redis从0开始计算
    System.out.println("list[1]=" + list1.index(1));
    System.out.println("list size=" + list1.size());
//        求链表下标区间成员,整个链表下标范围是0到size-1
    List range = list1.range(0, list1.size() - 1);
    System.out.println("遍历List");
    for (Object o : range) {
        System.out.println(o);
    }
    return new ResponseEntity<>(HttpStatus.OK);
}

上述操作基本是基于 StringRedisTemplate 的,所以保存到 Redis 服务器的都是字符串类型,只是这里有两点需要注意。首先是列表元素的顺序问题,是从左到右还是从右到左, 这是容易弄糊涂的问题: 其次是下标问题,在 Redis 中是以 0 开始的,这与 Java 中 的数组类似。

集合(Set)

对于集合, 在 Redis 中是不允许成员重复的,它在数据结构上是一个散列表的结构,所以对于它而言是无序的,对于两个或者以上的集合, Redis 还提供了交集、并集和差集的运算。

@GetMapping("/set")
public Object testSet() {
    stringRedisTemplate.opsForSet().add("set1", "v1", "v3", "v2", "v5");
    stringRedisTemplate.opsForSet().add("set2", "v1", "v2", "v3", "v4", "v5", "v8");
    stringRedisTemplate.opsForSet().add("set3", "v1", "a1", "f1", "d1", "e1");
    BoundSetOperations set1 = stringRedisTemplate.boundSetOps("set1");
    System.out.println("size:" + set1.size());
    System.out.println("add:" + set1.add("v1", "v3", "v6", "v4"));
    System.out.println("add-size:" + set1.size());
    System.out.println("remove:" + set1.remove("v1", "v7"));
//        求成员数
    System.out.println("remove-size:" + set1.size());
//        求交集
    Set intersect = set1.intersect("set2");
    System.out.println("交集=" + intersect.toString());
    set1.intersectAndStore("set2", "set2inter");
//        求差集
    Set set2 = set1.diff("set2");
    System.out.println("diff" + set2.toString());
//        求差集,并且用新集合diff保存
    set1.diffAndStore("set2", "set2diff");
//        求并集
    Set union = set1.union("set2");
    System.out.println("union=" + union.toString());
    set1.unionAndStore("set2", "set2union");
    return new ResponseEntity<>(HttpStatus.OK);
}

这里在添加集合 setl 时, 存在两个" vl "一样的元素。因为集合不允许重复,所以实际上在集合只 算是一个元素。然后可以看到对集合各类操作,在最后还有交集、差集和并集的操作,这些是集合最常用的操作。

有序集合(Set)

在一些网站中,经常会有排名,如最热门的商品或者最大的购买买家,都是常常见到的场景。 对于这类排名,刷新往往需要及时,也涉及较大的统计,如果使用数据库会太慢。为了支持集合的 排序, Redis 还提供了有序集合(zset)。有序集合与集合的差异并不大,它也是一种散列表存储的方 式,同时它的有序性只是靠它在数据结构中增加一个属性–score (分数)得以支持。为了支持这 个变化, Spring 提供了 TypedTuple 接口,它定义了两个方法,并且 Spring 还提供了其默认的实现类 DefaultTypedTuple。
在这里插入图片描述
在 TypedTuple 接口的设计中, value 是保存有序集合的值, score 则是保存分数, Redis 是使用分数完成集合的排序的,这样如果把买家作为一个有序集合,而买家花的钱作为分数,就可以使用Redis 进行快速排序了。

操作 Redis 有序集合
@GetMapping("/zset")
public Object testZset() {
    Set<ZSetOperations.TypedTuple<String>> typedTuples = new HashSet<>();
    for (int i = 0; i < 10; i++) {
//            分数
        double score = i * 0.1;
//            创建一个TypedTuple对象,存入值和分数score
        ZSetOperations.TypedTuple typedTuple = new DefaultTypedTuple<String>("v" + i, score);
        typedTuples.add(typedTuple);
    }
    Long zset1 = stringRedisTemplate.opsForZSet().add("zset1", typedTuples);
    System.out.println("zset1:" + zset1);
    BoundZSetOperations<String, String> zSetOps = stringRedisTemplate.boundZSetOps("zset1");
//        增加一个元素
    System.out.println("size:" + zSetOps.size());
    zSetOps.add("v10", 0.5);
    System.out.println("size-add:" + zSetOps.size());
    Set<String> range = zSetOps.range(1, -1);
    System.out.println("range=" + range.toString());
//        按分数排序获取有序集合
    Set<String> rangeByScore = zSetOps.rangeByScore(0.2, 0.6);
    System.out.println("rangeByScore=" + rangeByScore.toString());

//        定义值的范围,注意是值!不是分数!
    RedisZSetCommands.Range range1 = new RedisZSetCommands.Range();
    range1.gte("v3");
    range1.lte("v6");
    Set<String> rangeByLex = zSetOps.rangeByLex(range1);
    System.out.println("rangeByLex=" + rangeByLex.toString());
    System.out.println("remove=" + zSetOps.remove("v4", "v7"));
    System.out.println("v8 score=" + zSetOps.score("v8"));
//        在下标区间,按分数排序,同时返回value和score
    Set<ZSetOperations.TypedTuple<String>> tuples = zSetOps.rangeWithScores(1, 10);
    System.out.println("下标遍历");
    for (ZSetOperations.TypedTuple<String> tuple : tuples) {
        System.out.println(tuple.getValue() + "===" + tuple.getScore());
    }
//        在分数区间,按分数排序,同时返回value和score
    System.out.println("分数遍历");
    Set<ZSetOperations.TypedTuple<String>> rangeByScoreWithScores = zSetOps.rangeByScoreWithScores(0.1, 0.5);
    for (ZSetOperations.TypedTuple<String> rangeByScoreWithScore : rangeByScoreWithScores) {
        System.out.println(rangeByScoreWithScore.getValue() + "===" + rangeByScoreWithScore.getScore());
    }
//        按从大到小排序
    Set<String> reverseRange = zSetOps.reverseRange(2, 5);
    System.out.println("reverseRange=" + reverseRange.toString());
    return new ResponseEntity<>(HttpStatus.OK);
}

代码中使用了 TypedTuple 保存有序集合的元素,在默认的情况下,有序集合是从小到大排序的,按下标、分数和值进行排序获取有序集合的元素,或者连同分数一起返回,有时候还可以进行从大到小的排序,只是在使用值排序时,我们可以使用 Spring 为我们创建的 Range 类,它可以定义值的范围【不是分数!!!】,还有大于、等于、大于等于、小于等于等范围定义,方便我们筛选对应的元素。
本案例中的代码Spring Boot2.x整合Spring Data Redis

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章