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

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