【Redis】4. Spring Boot整合Redis

912 阅读8分钟

Spring Data Redis

Spring Data为方便Spring Boot操作Redis开源了Spring Data Redis项目,该项目中封装了RedisTemplate和StringRedisTemplate。

在Spring Boot 1.x 时,Spring Data Redis底层是使用Jedis操作Redis的,在Spring Boot 2.0 之后,Spring Data Redis底层使用Lettuce操作Redis了。

其中StringRedisTemplate是RedisTemplate的子类,两个类的方法基本一致,不同之处主要体现在操作的数据类型不同

RedisTemplate中的两个泛型都是Object,意味着存储的Key和Value都可以是一个对象;而StringRedisTemplate的两个泛型都是String,意味着StringRedisTemplate的Key和Value都只能是字符串。

如果你明确本次存入的Key和Value都是字符串,那么就使用StringRedisTemplate;如果你明确本次存入的Key和Value中有Object,那么就使用RedisTemplate。

在之前的学习中,我们知道Redis本身是一个Key-Value数据库,其中的Key和Value只能存储字符串。例如String类型一般体现为"key"="value";List类型一般体现为"key"=["value1","value2","value3"]。但是在Java集成Redis时,我们知道Java中万物皆对象,那么Redis如何将Java中的对象作为Key或者是Value呢?

Spring Data Redis提供了多种自动序列化和自动反序列化的方式,可以将Java对象自动转换成字符串存入Redis,也可以将Redis存储的字符串自动转换成Java对象应用在Spring Boot中。

实现自动序列化和自动反序列化有一个重要的前提,对象的类必须实现序列化接口

环境搭建

  1. pom.xml 引入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
  2. application.yml 配置

    spring:
      # Redis配置
      redis:
        host: localhost
        port: 6379
        # 使用0号库
        database: 0
        # 配置Redis连接池
        lettuce:
          pool:
            # 连接池最大链接数默认值为8
            max-active: 8
            # 连接池最大阻塞时间(使用负值表示没有限制)默认为-1
            max-wait: -1
            # 连接池中的最大空闲连接数 默认为8
            max-idle: 8
            # 连接池中的最小空闲连接数 默认为8
            min-idle: 0
    
  3. 注入模板

    @Autowired
    private RedisTemplate redisTemplate;
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    

操作Key相关API

1. 删除Key

Boolean result = stringRedisTemplate.delete("key");

2. Key是否存在

Boolean result = stringRedisTemplate.hasKey("key");

3. 查看Key类型

DataType type = stringRedisTemplate.type("key");

4. 获取所有Key

Set<String> keys = stringRedisTemplate.keys("*");

5. 查看Key到期时间

Long expire = stringRedisTemplate.getExpire("key");

永久存储的Key返回-1,Key不存在返回-2。

6. 随机获取Key

String randomKey = stringRedisTemplate.randomKey();

7. Key改名

stringRedisTemplate.rename("oldKey", "newKey");

该API要求newKey必须不存在,如果不存在就修改,存在则报错。

也可以使用如下API,该API会在改oldKey前判断newKey是否不存在,如果不存在就修改并返回true,存在就不修改并返回false。

Boolean reuslt = stringRedisTemplate.renameIfAbsent("oldKey", "newKey");

8. 移动Key

Boolean result = stringRedisTemplate.move("key", 1);

第二个参数为:dbIndex,表示库号。

StringRedisTemplate和RedisTemplate

1. 关系

StringRedisTemplate是RedisTemplate的子类。

2. 区别

20210921182119.png

  • 保存Key-Value使用的序列化策略不同,StringRedisTemplate默认采用String的序列化策略(StringRedisSerializer),RedisTemplate默认采用JDK的序列化策略(JdkSerializationRedisSerializer)。
    • String序列化策略将Key-Value序列化成明文字符串,在Redis中以 "name" 的形式存储。
    • JDK序列化策略会将Key-Value序列化成字节数组,在Redis中以 "\xAD\xED\x00" 的形式存储。
  • 二者数据不共通,StringRedisTemplate只能管理StringRedisTemplate中的数据,RedisTemplate只能管理RedisTemplate中的数据。因为StringRedisTemplate无法处理以字节数组形式保存的数据,RedisTemplate无法处理以字符串明文形式保存的数据(因此使用StringRedisTemplate保存的数据不用用RedisTemplate来取,反之同理)。
  • StringRedisTemplate的Key和Value必须都是String类型(泛型是<String, String>),RedisTemplate的Key和Value都是Object类型(泛型是<Object, Object>,自定义Object必须实现序列化接口)。

3. 选择

如果要在Redis中存取的数据是String类型数据,就用StringRedisTemplate。

如果要在Redis中存取的数据是复杂的对象类型数据,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择。

RedisTempate序列化

由上内容我们可知,RedisTemplate默认使用JDK序列化。

该序列化规则是:无论你的Key-Value是什么Object类型,比如String,List,Map或者自定义Bean,只要使用RedisTemplate进行存储,都会被序列化为字节数组。Value被序列化为字节数组其实没有什么影响,但是如果Key也被序列化为字节数组后,那么通过Redis客户端就无法通过Key去获取Value了。因为你需要提供序列化后Key的字节数组形式才可以获取对应的Value,如果想获取Value,只有通过Java才能获取了,这样其实很不方便。

在一般场景中,只有Value需要使用Object来存储,Key只需要是String就够了。如果要让Value是Object类型,我们就需要使用RedisTemplate,但是RedisTemplate的默认序列化方案会是Key和Value都会被JDK序列化。为了获取方便,我们想让Value是Object的同时,Key还不序列化,因此,我们在使用RedisTemplate时,会手动修改RedisTemplate中Key的默认序列化方案为String序列化,Value的默认序列化方案还是JDK序列化,保持不变

尤其要注意的一点是:在RedisTemplate操作Hash数据类型中,Hash的field默认序列化方案也是JDK序列化,所以如果使用RedisTemplate操作Hash数据类型,我们还需要手动修改Hash中field的默认序列化方案为String序列化

Redis中所有序列化方案(列举几个常见的):

20210921184350.png

opsFor方法

20210920095127.png

20210921161857.png

opsFor方法是Spring Data Redis封装的API的前置方法,无论是RedisTemplate还是StringRedisTemplate,调用任何操作数据的API都需要先调用opsFor方法来选择组织和操作数据的类型。

  • opsForValue:组织和操作数据的数据结构类型是String类型。
  • opsForList:组织和操作数据的数据结构类型是List类型。
  • opsForSet:组织和操作数据的数据结构类型是Set类型。
  • opsForZSet:组织和操作数据的数据结构类型是ZSet类型。
  • opsForHash:组织和操作数据的数据结构类型是Hash类型。

比方说,opsForValue提供的API底层都是按照Redis中的String类型来组织和处理数据,opsForList提供的API底层都是按照Redis中的List类型来组织和处理数据。但是在StringRedisTemplate下调用的opsFor方法组织和处理的数据都是String;RedisTemplate下调用的opsFor方法组织和处理的数据都是Object(包含String)。前者的String是Redis中的数据组织类型,后者的String是Java中的数据类型。

StringRedisTemplate相关API

1. 操作String

API说明示例
set设置一对Key-ValuestringRedisTemplate.opsForValue().set("key", "value");
set设置一对Key-Value(设置到期时间)stringRedisTemplate.opsForValue().set("key", "value"", 60, TimeUnit.SECONDS);
get获取ValuestringRedisTemplate.opsForValue().get("key");
append追加ValuestringRedisTemplate.opsForValue().append("key", "value");

2. 操作List

API说明示例
leftPush将Value放入List(从左往右放),Key不存在则创建ListstringRedisTemplate.opsForList().leftPush("key", "value");
leftPushAll将若干个Value放入List(从左往右放),Key不存在则创建ListstringRedisTemplate.opsForList().leftPushAll("key", "value1", "value2");
range通过List的下标,获取部分List中的Value(表头Value下标为0)stringRedisTemplate.opsForList().range("key", 0, -1);
index通过下标获取ValuestringRedisTemplate.opsForList().index("key", 0);
leftPop获取List表头的Value,并移除表头ValuestringRedisTemplate.opsForList().leftPop("key");
rightPop获取List表尾的Value,并移除表尾ValuestringRedisTemplate.opsForList().rightPop("key");
trim通过List的下标,只保留指定部分Value,其他全部删除stringRedisTemplate.opsForList().trim("key", 1, 3);

3. 操作Set

API说明示例
add将若干个Value放入Set中(重复的会覆盖),Key不存在则会创建SetstringRedisTemplate.opsForSet().add("key", "value1", "value2");
members查看Set中所有ValuestringRedisTemplate.opsForSet().members("key");
move将Value从一个Set中转移到另一个SetstringRedisTemplate.opsForSet().move("key1", "value", "key2");
pop随机返回Set中若干个Value并移除stringRedisTemplate.opsForSet().pop("key");
size返回Set中Value的数量stringRedisTemplate.opsForSet().size("key");

4. 操作ZSet

API说明示例
add将若干个Value和对应的分数放入ZSet中(重复的会覆盖),Key不存在则会创建ZSetstringRedisTemplate.opsForZSet().add("key", "value", 1);
range通过ZSet的下标,升序获取部分ZSet中的Value(分数最低的Value下标为0),可以显示分数stringRedisTemplate.opsForZSet().range("key", 0, 3);
rangeByScoreWithScores通过ZSet的分数,升序获取部分ZSet中的Value,可以显示分数stringRedisTemplate.opsForZSet().rangeByScoreWithScores("key", 0, 100);

5. 操作Hash

API说明示例
put在Hash中添加一对Field-Value,Key不存在则会创建Hash(Field存在则会覆盖)stringRedisTemplate.opsForHash().put("key", "field", "value");
get获取Hash中的某一个ValuestringRedisTemplate.opsForHash().get("key", "field");
keys查看Hash中所有FieldstringRedisTemplate.opsForHash().keys("key");
values查看Hash中所有ValuestringRedisTemplate.opsForHash().values("key");
putAll在Hash中添加若干对Field-Value(Field存在则会覆盖)stringRedisTemplate.opsForHash().putAll("key", new HashMap<String, String>());
multiGet通过Field查看若干个ValuestringRedisTemplate.opsForHash().multiGet("key", Arrays.asList("field1", "field2"));

RedisTemplate相关API

RedisTemplate封装的API和StringRedisTemplate封装的API一致,只是作为参数的Value类型不同。

bound API

Spring Data Redis为了方便我们对同一个Key的Value进行多次操作,为StringRedisTemplate和RedisTemplate提供了一套bound API(绑定API)。

20210921203530.png

以下使用StringRedisTemplate的bound API进行演示。

如果不使用bound API:

stringRedisTemplate.opsForValue().set("key", "value");
stringRedisTemplate.opsForValue().append("key", " is a value");
String key = stringRedisTemplate.opsForValue().get("key");

使用bound API:

BoundValueOperations<String, String> keyValueOps = stringRedisTemplate.boundValueOps("key");
keyValueOps.set("value");
keyValueOps.append(" is a value");
String key = keyValueOps.get();