深入理解 Redis Template及4种序列化方式__spring boot整合redis实现RedisTemplate三分钟快速入门
概述
使用Spring 提供的 Spring Data Redis 操作redis 必然要使用Spring提供的模板类 RedisTemplate, 今天我们好好的看看这个模板类 。
RedisTemplate
看看4个序列化相关的属性 ,主要是 用于 KEY 和 VALUE 的序列化 。 举个例子,比如说我们经常会将POJO 对象存储到 Redis 中,一般情况下会使用 JSON 方式序列化成字符串,存储到 Redis 中 。
Spring提供的Redis数据结构的操作类
- ValueOperations 类,提供 Redis String API 操作
- ListOperations 类,提供 Redis List API 操作
- SetOperations 类,提供 Redis Set API 操作
- ZSetOperations 类,提供 Redis ZSet(Sorted Set) API 操作
- GeoOperations 类,提供 Redis Geo API 操作
- HyperLogLogOperations 类,提供 Redis HyperLogLog API 操作
StringRedisTemplate
再看个常用的 StringRedisTemplate
RedisTemplate<K, V> 支持泛型,StringRedisTemplate K V 均为String类型。
org.springframework.data.redis.core.StringRedisTemplate
继承 RedisTemplate 类,使用 org.springframework.data.redis.serializer.StringRedisSerializer
字符串序列化方式。
RedisSerializer 序列化 接口
RedisSerializer接口 是 Redis 序列化接口,用于 Redis KEY 和 VALUE 的序列化
RedisSerializer 接口的实现类 如下
归类一下
- JDK 序列化方式 (默认)
- String 序列化方式J
- SON 序列化方式
- XML 序列化方式
JDK 序列化方式 (默认)
org.springframework.data.redis.serializer.JdkSerializationRedisSerializer
,默认情况下,RedisTemplate 使用该数据列化方式。
我们来看下源码 RedisTemplate#afterPropertiesSet()
Spring Boot 自动化配置 RedisTemplate Bean 对象时,就未设置默认的序列化方式。
绝大多数情况下,不推荐使用 JdkSerializationRedisSerializer 进行序列化。主要是不方便人工排查数据。
我们来做个测试
运行单元测试
看不懂呀 ,老哥
KEY 前面带着奇怪的 16 进制字符 , VALUE 也是一串奇怪的 16 进制字符 。。。。。
为什么是这样一串奇怪的 16 进制? ObjectOutputStream#writeString(String str, boolean unshared) 实际就是标志位 + 字符串长度 + 字符串内容
KEY 被序列化成这样,线上通过 KEY 去查询对应的 VALUE非常不方便,所以 KEY 肯定是不能被这样序列化的。
VALUE 被序列化成这样,除了阅读可能困难一点,不支持跨语言外,实际上也没还OK。不过,实际线上场景,还是使用 JSON 序列化居多。
String 序列化方式
org.springframework.data.redis.serializer.StringRedisSerializer
,字符串和二进制数组的直接转换
绝大多数情况下,我们 KEY 和 VALUE 都会使用这种序列化方案。
JSON 序列化方式
org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
使用 Jackson 实现 JSON 的序列化方式,并且从 Generic 单词可以看出,是支持所有类。
public GenericJackson2JsonRedisSerializer(@Nullable String classPropertyTypeName) {
.....
.....
if (StringUtils.hasText(classPropertyTypeName)) {
mapper.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, classPropertyTypeName);
} else {
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
}
}
classPropertyTypeName 不为空的话,使用传入对象的 classPropertyTypeName 属性对应的值,作为默认类型(Default Typing) ,否则使用传入对象的类全名,作为默认类型(Default Typing)。
我们来思考下,在将一个对象序列化成一个字符串,怎么保证字符串反序列化成对象的类型呢?Jackson 通过 Default Typing ,会在字符串多冗余一个类型,这样反序列化就知道具体的类型了
先说个结论
标准JSON
{
"id": 100,
"name": "小工匠",
"sex": "Male"
}
使用 Jackson Default Typing 机制序列化
{
"@class": "com.artisan.domain.Artisan",
"id": 100,
"name": "小工匠",
"sex": "Male"
}
示例
测试一把
【配置类】
@Bean
public RedisTemplate<String, Object> redisTemplate() {
// 创建 RedisTemplate 对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置 RedisConnection 工厂。 它就是实现多种 Java Redis 客户端接入的秘密工厂
template.setConnectionFactory(connectionFactory);
// 使用 String 序列化方式,序列化 KEY 。
template.setKeySerializer(RedisSerializer.string());
// 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。
template.setValueSerializer(RedisSerializer.json());
return template;
}
【单元测试】
@Bean
public RedisTemplate<String, Object> redisTemplate() {
// 创建 RedisTemplate 对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置 RedisConnection 工厂。 它就是实现多种 Java Redis 客户端接入的秘密工厂
template.setConnectionFactory(connectionFactory);
// 使用 String 序列化方式,序列化 KEY 。
template.setKeySerializer(RedisSerializer.string());
// 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。
template.setValueSerializer(RedisSerializer.json());
return template;
}
【结果】
是不是多了@class 属性,反序列化的对象的类型就可以从这里获取到。
@class 属性看似完美解决了反序列化后的对象类型,但是带来 JSON 字符串占用变大,所以实际项目中,我们很少采用 Jackson2JsonRedisSerializer
XML 序列化方式
org.springframework.data.redis.serializer.OxmSerializer
使用 Spring OXM 实现将对象和 String 的转换,从而 String 和二进制数组的转换。 没见过哪个项目用过,不啰嗦了
spring boot整合redis实现RedisTemplate三分钟快速入门
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
RedisTemplate五种数据结构的操作
- redisTemplate.opsForValue(); //操作字符串
- redisTemplate.opsForHash(); //操作hash
- redisTemplate.opsForList(); //操作list
- redisTemplate.opsForSet(); //操作set
- redisTemplate.opsForZSet(); //操作有序zset
RedisTemplate方法讲解
判断key是否存在
/**
* 判断key是否存在
*/
@GetMapping("haskey")
public boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
获取指定的key的失效时间
/**
* 指定key的失效时间
*/
@GetMapping("expire")
public void expire(String key, long time) {
//参数一:key
//参数二:睡眠时间
//参数三:睡眠时间单位 TimeUnit.DAYS 天 TimeUnit.HOURS 小时 。。。
redisTemplate.expire(key, time, TimeUnit.MINUTES);
}
根据key获取过期时间
/**
* 根据key获取过期时间
*/
@GetMapping("getexpire")
public long getExpire(String key) {
Long expire = redisTemplate.getExpire(key);
return expire;
}
根据key删除reids中缓存数据
/**
* 根据key删除reids中缓存数据
*/
@GetMapping("delredisbykey")
public void delete(String key) {
redisTemplate.delete(key);
}
保存和读取String
/**
* 保存和读取String
*/
@GetMapping("stringredisdemo")
public String stringredisdemo() {
//设置过期时间为1分钟
redisTemplate.opsForValue().set("key1", "value1", 1, TimeUnit.MINUTES);
redisTemplate.opsForValue().set("key2", "value2");
redisTemplate.opsForValue().set("key3", "value3");
//读取redis数据
String result1 = redisTemplate.opsForValue().get("key1").toString();
String result2 = redisTemplate.opsForValue().get("key2").toString();
String result3 = redisTemplate.opsForValue().get("key3").toString();
System.out.println("缓存结果为:result:" + result1 + " " + result2 + " " + result3);
return "缓存结果为:result:" + result1 + " " + result2 + " " + result3;
}
保存和读取list
/**
* 保存和读取list
*/
@GetMapping("listredisdemo")
public String listredisdemo() {
List<String> list1 = new ArrayList<>();
list1.add("a1");
list1.add("a2");
list1.add("a3");
List<String> list2 = new ArrayList<>();
list2.add("b1");
list2.add("b2");
list2.add("b3");
redisTemplate.opsForList().leftPush("listkey1", list1);
redisTemplate.opsForList().rightPush("listkey2", list2);
List<String> resultList1 = (List<String>) redisTemplate.opsForList().leftPop("listkey1");
List<String> resultList2 = (List<String>) redisTemplate.opsForList().rightPop("listkey2");
System.out.println("resultList1:" + resultList1);
System.out.println("resultList2:" + resultList2);
return "成功";
}
Hash结构,保存和读取map
/**
* Hash结构,保存和读取map
*/
@GetMapping("mapredisdemo")
public String mapredisdemo() {
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
map.put("key4", "value4");
map.put("key5", "value5");
redisTemplate.opsForHash().putAll("map1", map);
Map<String, String> resultMap = redisTemplate.opsForHash().entries("map1");
List<String> reslutMapList = redisTemplate.opsForHash().values("map1");
Set<String> resultMapSet = redisTemplate.opsForHash().keys("map1");
String value = (String) redisTemplate.opsForHash().get("map1", "key1");
System.out.println("value:" + value);
System.out.println("resultMapSet:" + resultMapSet);
System.out.println("resultMap:" + resultMap);
System.out.println("resulreslutMapListtMap:" + reslutMapList);
return "成功";
}
保存和读取Set
/**
* 保存和读取Set
*/
@GetMapping("setredisdemo")
public String getredisdemo() {
SetOperations<String, String> set = redisTemplate.opsForSet();
set.add("key1", "value1");
set.add("key1", "value2");
set.add("key1", "value3");
Set<String> resultSet = redisTemplate.opsForSet().members("key1");
System.out.println("resultSet:" + resultSet);
return "resultSet:" + resultSet;
}
保存和读取zset
/**
* 保存和读取zset
*/
@GetMapping("zsetredisdemo")
public String zsetredisdemo() {
ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<>("zset-5", 9.6);
ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<>("zset-6", 9.9);
Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>();
tuples.add(objectTypedTuple1);
tuples.add(objectTypedTuple2);
System.out.println(redisTemplate.opsForZSet().add("zset1", tuples));
System.out.println(redisTemplate.opsForZSet().range("zset1", 0, -1));
return "成功";
}
完整示例代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
import java.util.concurrent.TimeUnit;
@RestController
public class ReidsDemo {
@Autowired
RedisTemplate redisTemplate;
/**
* 指定key的失效时间
*/
@GetMapping("expire")
public void expire(String key, long time) {
//参数一:key
//参数二:睡眠时间
//参数三:睡眠时间单位 TimeUnit.DAYS 天 TimeUnit.HOURS 小时 。。。
redisTemplate.expire(key, time, TimeUnit.MINUTES);
}
/**
* 根据key获取过期时间
*/
@GetMapping("getexpire")
public long getExpire(String key) {
Long expire = redisTemplate.getExpire(key);
return expire;
}
/**
* 判断key是否存在
*/
@GetMapping("haskey")
public boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
/**
* 根据key删除reids中缓存数据
*/
@GetMapping("delredisbykey")
public void delete(String key) {
redisTemplate.delete(key);
}
/**
* 保存和读取String
*/
@GetMapping("stringredisdemo")
public String stringredisdemo() {
//设置过期时间为1分钟
redisTemplate.opsForValue().set("key1", "value1", 1, TimeUnit.MINUTES);
redisTemplate.opsForValue().set("key2", "value2");
redisTemplate.opsForValue().set("key3", "value3");
//读取redis数据
String result1 = redisTemplate.opsForValue().get("key1").toString();
String result2 = redisTemplate.opsForValue().get("key2").toString();
String result3 = redisTemplate.opsForValue().get("key3").toString();
System.out.println("缓存结果为:result:" + result1 + " " + result2 + " " + result3);
return "缓存结果为:result:" + result1 + " " + result2 + " " + result3;
}
/**
* 保存和读取list
*/
@GetMapping("listredisdemo")
public String listredisdemo() {
List<String> list1 = new ArrayList<>();
list1.add("a1");
list1.add("a2");
list1.add("a3");
List<String> list2 = new ArrayList<>();
list2.add("b1");
list2.add("b2");
list2.add("b3");
redisTemplate.opsForList().leftPush("listkey1", list1);
redisTemplate.opsForList().rightPush("listkey2", list2);
List<String> resultList1 = (List<String>) redisTemplate.opsForList().leftPop("listkey1");
List<String> resultList2 = (List<String>) redisTemplate.opsForList().rightPop("listkey2");
System.out.println("resultList1:" + resultList1);
System.out.println("resultList2:" + resultList2);
return "成功";
}
/**
* Hash结构,保存和读取map
*/
@GetMapping("mapredisdemo")
public String mapredisdemo() {
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
redisTemplate.opsForHash().putAll("map1", map);
Map<String, String> resultMap = redisTemplate.opsForHash().entries("map1");
List<String> reslutMapList = redisTemplate.opsForHash().values("map1");
Set<String> resultMapSet = redisTemplate.opsForHash().keys("map1");
String value = (String) redisTemplate.opsForHash().get("map1", "key1");
System.out.println("value:" + value);
System.out.println("resultMapSet:" + resultMapSet);
System.out.println("resultMap:" + resultMap);
System.out.println("resulreslutMapListtMap:" + reslutMapList);
return "成功";
}
/**
* 保存和读取Set
*/
@GetMapping("setredisdemo")
public String getredisdemo() {
SetOperations<String, String> set = redisTemplate.opsForSet();
set.add("key1", "value1");
set.add("key1", "value2");
set.add("key1", "value3");
Set<String> resultSet = redisTemplate.opsForSet().members("key1");
System.out.println("resultSet:" + resultSet);
return "resultSet:" + resultSet;
}
/**
* 保存和读取zset
*/
@GetMapping("zsetredisdemo")
public String zsetredisdemo() {
ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<>("zset-5", 9.6);
ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<>("zset-6", 9.9);
Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>();
tuples.add(objectTypedTuple1);
tuples.add(objectTypedTuple2);
System.out.println(redisTemplate.opsForZSet().add("zset1", tuples));
System.out.println(redisTemplate.opsForZSet().range("zset1", 0, -1));
return "成功";
}
}
序列化
直接粘贴在项目中即可
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/*
*序列化
*/
@Configuration
public class MyRedisConfig {
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//参照StringRedisTemplate内部实现指定序列化器
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
return redisTemplate;
}
private RedisSerializer<String> keySerializer(){
return new StringRedisSerializer();
}
//使用Jackson序列化器
private RedisSerializer<Object> valueSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}
相关文章
- spring boot自动配置原理面试题_Spring boot面试
- spring boot jedis_spring整合redis详解
- spring boot集成redis基础入门实例详解
- 一文搞懂阿里云服务器部署Redis并整合Spring Boot
- Spring之Redis访问(Spring-data-redis)详解数据库
- Spring整合Redis简单实现高效缓存(spring集成redis)
- 定期清理Redis,有效保护数据安全(怎么定期清理redis)
- 存储微博数据的Redis应该采用什么格式(微博存redis什么格式)
- Redis给结构化数据库带来的新变化(结构化数据库 redis)
- 架构师精讲Redis视频教程(架构师redis教学视频)
- 春满活力Redis存储专业解决方案(spring.redis)
- 春天即将到来,伴随着Redis技术的点缀(spring.redis)
- 分布式Redis封装最佳实践(分布式项目redis封装)
- Redis事务一切皆可管理(什么是redis 事务)
- 双剑合璧一台电脑同时支持主从Redis(同一台电脑主从redis)
- 取消Redis服务器配置的密码保护(取消redis的密码)
- 利用Redis锁解决竞争问题(redis 锁作用)
- Redis链接池优化精准的配置与调整(redis链接池配置)
- Redis主从复制正确配置保障数据安全(redis配置主从关系)
- Redis连接池出错谨防死锁(redis连接池报错)
- Redis过期处理机制利用多线程提升效率(redis过期 多线程)