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中。
实现自动序列化和自动反序列化有一个重要的前提,对象的类必须实现序列化接口。
环境搭建
-
pom.xml
引入依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
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
-
注入模板
@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. 区别
- 保存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中所有序列化方案(列举几个常见的):
opsFor方法
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-Value | stringRedisTemplate.opsForValue().set("key", "value"); |
set | 设置一对Key-Value(设置到期时间) | stringRedisTemplate.opsForValue().set("key", "value"", 60, TimeUnit.SECONDS); |
get | 获取Value | stringRedisTemplate.opsForValue().get("key"); |
append | 追加Value | stringRedisTemplate.opsForValue().append("key", "value"); |
2. 操作List
API | 说明 | 示例 |
---|---|---|
leftPush | 将Value放入List(从左往右放),Key不存在则创建List | stringRedisTemplate.opsForList().leftPush("key", "value"); |
leftPushAll | 将若干个Value放入List(从左往右放),Key不存在则创建List | stringRedisTemplate.opsForList().leftPushAll("key", "value1", "value2"); |
range | 通过List的下标,获取部分List中的Value(表头Value下标为0) | stringRedisTemplate.opsForList().range("key", 0, -1); |
index | 通过下标获取Value | stringRedisTemplate.opsForList().index("key", 0); |
leftPop | 获取List表头的Value,并移除表头Value | stringRedisTemplate.opsForList().leftPop("key"); |
rightPop | 获取List表尾的Value,并移除表尾Value | stringRedisTemplate.opsForList().rightPop("key"); |
trim | 通过List的下标,只保留指定部分Value,其他全部删除 | stringRedisTemplate.opsForList().trim("key", 1, 3); |
3. 操作Set
API | 说明 | 示例 |
---|---|---|
add | 将若干个Value放入Set中(重复的会覆盖),Key不存在则会创建Set | stringRedisTemplate.opsForSet().add("key", "value1", "value2"); |
members | 查看Set中所有Value | stringRedisTemplate.opsForSet().members("key"); |
move | 将Value从一个Set中转移到另一个Set | stringRedisTemplate.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不存在则会创建ZSet | stringRedisTemplate.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中的某一个Value | stringRedisTemplate.opsForHash().get("key", "field"); |
keys | 查看Hash中所有Field | stringRedisTemplate.opsForHash().keys("key"); |
values | 查看Hash中所有Value | stringRedisTemplate.opsForHash().values("key"); |
putAll | 在Hash中添加若干对Field-Value(Field存在则会覆盖) | stringRedisTemplate.opsForHash().putAll("key", new HashMap<String, String>()); |
multiGet | 通过Field查看若干个Value | stringRedisTemplate.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)。
以下使用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();