Spring、Spring Boot集成Spring Data Redis以及相关API的使用
Spring Data Redis
概述
Spring-data-redis是spring家族的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。
Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用。可以在Redis官网下载。
高度封装的RedisTemplate类
针对jedis客户端中大量api进行了归类封装,提供了一个高度封装的RedisTemplate类,包含了对5种数据类型的操作
// 操作字符串
RedisTemplate redisTemplate.opsForValue()
// 操作hash
RedisTemplate redisTemplate.opsForHash()
// 操作list
RedisTemplate redisTemplate.opsForList()
// 操作set
RedisTemplate redisTemplate.opsForSet()
// 操作有序set
RedisTemplate redisTemplate.opsForZSet()
Lettuce和Jedis的区别
Jedis是一个优秀的基于Java语言的Redis客户端。但是:Jedis在实现上是直接连接 Redis Server,在多个线程间共享一个Jedis实例时是线程不安全的,如果要在多线程场景下使用Jedis,需要使用连接池,每个线程都使用自己的Jedis 实例,当连接数量增多时,会消耗较多的物理资源。
Lettuce是基于Netty的连接,是一个可伸缩的线程安全的Redis 客户端,支持同步、异步和响应式模式。多个线程可以共享一个连接实例,而不必担心多线程并发问题。它基于优秀Netty NIO框架构建,支持Redis的高级功能,如Sentinel,集群,流水线,自动重新连接和 Redis 数据模型。
注意:
1.x的版本默认采用的连接池技术是Jedis
2.0以上版本默认连接池是Lettuce
Spring集成Spring Data Redis
引入依赖
<dependencies>
<!-- 缓存 Redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<!--spring-data-redis-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.9.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
创建redis-config.properties
#redisIP地址
redis.host=127.0.0.1
#redis端口
redis.port=6379
#redis密码
redis.pass=
#选择使用的DB
redis.database=6
#最大空闲数
redis.maxIdle=200
#连接时的最大等待毫秒数
redis.maxWait=2000
#在获取jedis实例时,是否进行验证操作;如果为true,则得到的jedis实例均是可用的
redis.testOnBorrow=true
创建spring-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!--加载解析配置文件-->
<context:property-placeholder location="classpath:redis-config.properties" />
<!--Jedis连接池配置-->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>
<!--
Jedis连接工厂对象
host-name:redis的IP
port:端口号
password:密码
pool-config-re:连接池信息配置
-->
<bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}"
p:port="${redis.port}"
p:password="${redis.pass}"
p:pool-config-ref="poolConfig"/>
<!--Jedis对缓存操作的模板对象-->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<!--配置数据源-->
<property name="connectionFactory" ref="JedisConnectionFactory" />
</bean>
</beans>
String类型操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class ValueTest {
@Autowired
private RedisTemplate redisTemplate;
/***
* 增加数据测试
*/
@Test
public void testAdd() {
//boundValueOps用于操作简单的key:value类型
redisTemplate.boundValueOps("value").set("小白");
}
/***
* 查询操作
*/
@Test
public void testGet() {
Object value = redisTemplate.boundValueOps("value").get();
System.out.println(value);
}
/***
* 删除测试
*/
@Test
public void testDelete() {
//redisTemplate.boundValueOps("value")
redisTemplate.delete("value");
}
}
设置过期时间
存值时指定过期时间和时间单位,其他类型操作类似。
/**
* 存值,同时设置过期时间
*/
@Test
public void setValue(){
redisTemplate.boundValueOps("name").set("小白");
redisTemplate.boundValueOps("name").expire(10,TimeUnit.SECONDS);
}
Set类型操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class SetTest {
@Autowired
private RedisTemplate redisTemplate;
/***
* 增加数据测试
*/
@Test
public void testAdd() {
redisTemplate.boundSetOps("set").add("小白");
redisTemplate.boundSetOps("set").add("小黑");
redisTemplate.boundSetOps("set").add("小白");
redisTemplate.boundSetOps("set").add("小黑");
}
/***
* 查询操作
*/
@Test
public void testGet() {
Set members = redisTemplate.boundSetOps("set").members();
System.out.println(members);
}
/***
* 删除测试
*/
@Test
public void testDelete() {
redisTemplate.boundSetOps("set").remove("小黑");
}
/***
* 删除整个集合
*/
@Test
public void testDeleteAll() {
redisTemplate.delete("set");
}
}
List类型操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class ListTest {
@Autowired
private RedisTemplate redisTemplate;
/**
* 左压栈:后添加的对象排在前边
* 从队列的左边开始增加数据,
*/
@Test
public void testLeftAdd() {
//只添加1条数据
redisTemplate.boundListOps("list").leftPush("小白1");
//批量添加
redisTemplate.boundListOps("list").leftPushAll("小白2", "小白3", "小白4");
}
/***
* 查询数据
*/
@Test
public void testGetValueLeft() {
List list = redisTemplate.boundListOps("list").range(0, 10);
System.out.println(list);
}
/**
* 移除集合某个元素
*/
@Test
public void testRemoveByIndex() {
redisTemplate.boundListOps("list").remove(1, "小白1");
}
/**
* 查询集合某个元素
*/
@Test
public void testSearchByIndex() {
String s = (String) redisTemplate.boundListOps("list").index(1);
System.out.println(s);
}
/***
* 左压出栈
*/
@Test
public void testLeftGet() {
Object result = redisTemplate.boundListOps("list").leftPop();
System.out.println(result);
}
/**
* 右压栈
*/
@Test
public void testRightAdd() {
//只添加1条数据
redisTemplate.boundListOps("list").rightPush("小白1");
//批量添加
redisTemplate.boundListOps("list").rightPushAll("小白2", "小白3", "小白4");
}
/***
* 查询数据
*/
@Test
public void testGetValueRight() {
List list = redisTemplate.boundListOps("list").range(0, 10);
System.out.println(list);
}
/***
* 右压出栈
*/
@Test
public void testRightGet() {
Object result = redisTemplate.boundListOps("list").rightPop();
System.out.println(result);
}
}
Hash类型操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class HashTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void testAdd() {
redisTemplate.boundHashOps("hash").put("1", "小白1");
redisTemplate.boundHashOps("hash").put("2", "小白2");
redisTemplate.boundHashOps("hash").put("3", "小白3");
redisTemplate.boundHashOps("hashs").put("1", "小白1");
redisTemplate.boundHashOps("hashs").put("2", "小白2");
redisTemplate.boundHashOps("hashs").put("2", "小白3");
}
/***
* 根据KEY提取值
*/
@Test
public void testGet() {
Object result1 = redisTemplate.boundHashOps("hash").get("1");
System.out.println(result1);
Object result2 = redisTemplate.boundHashOps("hashs").get("1");
System.out.println(result2);
}
/***
* 根据KEY移除值
*/
@Test
public void testDelete() {
redisTemplate.boundHashOps("hash").delete("2");
}
/***
* 查询所有所有值
*/
@Test
public void testSetValue() {
List nameSpace = redisTemplate.boundHashOps("hash").values();
for (Object o : nameSpace) {
System.out.println(o);
}
}
/***
* 查询所有key
*/
@Test
public void testGetKeys(){
Set s = redisTemplate.boundHashOps("hash").keys();
System.out.println(s);
}
}
ZSet类型操作
zset是set的升级版本,它在set的基础上增加了一格顺序属性,这一属性在添加元素 的同时可以指定,每次指定后,zset会自动重新按照新的值调整顺序。可以理解为有两列 的mysql表,一列存储value,一列存储分值。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-redis.xml")
public class ValueTest {
@Autowired
private RedisTemplate redisTemplate;
/***
* 存值 ,指定分值
*/
@Test
public void setValue() {
redisTemplate.boundZSetOps("zset").add("小白",100);
redisTemplate.boundZSetOps("zset").add("小黑",50);
redisTemplate.boundZSetOps("zset").add("小红",80);
}
/***
* 查询,由低到高
*/
@Test
public void getValue1() {
Set namezset = redisTemplate.boundZSetOps("set").range(0,‐1);
}
/***
* 查询,由高到低,取前10
*/
@Test
public void getValue2() {
Set namezset =redisTemplate.boundZSetOps("set").reverseRange(0,10);
}
/***
* 增加分数
*/
@Test
public void addScore() {
redisTemplate.boundZSetOps("set").incrementScore("小黑",100);
}
/***
* 查询值和分数
*/
@Test
public void addScore() {
Set<ZSetOperations.TypedTuple> set=redisTemplate.boundZSetOps("set").reverseRangeWithScores(0, ‐1);
for(ZSetOperations.TypedTuple typedTuple:set){
System.out.print("姓名:"+typedTuple.getValue());
System.out.println("分数:"+typedTuple.getScore());
}
}
StringRedisTemplate与RedisTemplate
1.StringRedisTemplate继承RedisTemplate。StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。
2.StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的,RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。
3.当存取字符串类型数据时,使用StringRedisTemplate;当数据是复杂的对象类型时,取出时又不想做任何数据转换,直接从Redis里面取出一个对象,则使用RedisTemplate。
自定义模板解决序列化问题
序列化方案
RedisTemplate<Object, Object>
默认序列化使用的是JdkSerializationRedisSerializer
,存储二进制字节码。通常需要自定义模板,当自定义模板后又想存储String 字符串时,可以使StringRedisTemplate的方式。
1.JdkSerializationRedisSerializer
使用JDK提供的序列化功能。 优点是反序列化时不需要提供类型信息(class),但缺点是序列化后的结果非常庞大,是JSON格式的5倍左右,会消耗 Redis服务器的大量内存。
2.Jackson2JsonRedisSerializer
使用Jackson库将对象序列化为JSON字符串。优点是速度快,序列化后的字符串短小精悍。但缺点也非常致命,那就是类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。
3.GenericJackson2JsonRedisSerializer
GenericJackson2JsonRedisSerializer通用型序列化,这种序列化方式不用自己手动指定对象的 Class。
自定义模板
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
//为string类型的key设置序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
//为string类型的value设置序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//为hash类型的key设置序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//为hash类型的value设置序列化
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
return redisTemplate;
}
}
序列化测试
@Test
public void testSerial(){
User user = new User();
user.setId(1);
user.setName("zhangsan");
user.setAge(20);
ValueOperations ops = redisTemplate.opsForValue();
ops.set("user",user);
Object user1 = ops.get("user");
System.out.println(user1);
}
Spring Boot集成Spring Data Redis
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
注意:
1.x的版本默认采用的连接池技术是Jedis;2.0以上版本默认连接池是Lettuce,如果采用Jedis,需要排除Lettuce的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependency>
application.yml配置
spring:
redis:
# Redis服务器地址
host: 127.0.0.1
# Redis服务器端口
port: 6379
# Redis认证密码
password: root
# Redis数据库
database: 0
# 连接超时时间
timeout: 10000ms
lettuce:
pool:
# 最大连接数,默认8
max-active: 1024
# 最大连接阻塞等待时间,单位毫秒,默认-1ms
max-wait: 10000ms
# 最大空闲连接,默认8
max-idle: 200
# 最小空闲连接,默认0
min-idle: 5
集成测试
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 测试连接
*/
@Test
public void initConn() {
ValueOperations ops = redisTemplate.opsForValue();
ops.set("name","zhangsan");
ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
stringStringValueOperations.set("age","20");
String age = stringStringValueOperations.get("age");
System.out.println(age);
}
/**
* 获取所有的key
*/
@Test
public void testAllKey(){
//获取当前数据库所有key
Set keys = redisTemplate.keys("*");
keys.forEach(System.out::println);
}
/**
* 失效时间
*/
@Test
public void testExpire(){
ValueOperations ops = redisTemplate.opsForValue();
//方法一,添加key的时候设置失效时间
// ops.set("code","test",30, TimeUnit.SECONDS);
//方法二,给已经存在的key设置失效时间
redisTemplate.expire("address",30, TimeUnit.SECONDS);
//查看失效时间
Long expire = redisTemplate.getExpire("address");
System.out.println(expire);
}
}
String类型操作
@Test
public void testString(){
ValueOperations ops = redisTemplate.opsForValue();
//添加一条数据
ops.set("name","zhangsan");
//获取一条数据
String name = (String) ops.get("name");
System.out.println(name);
//层级关系,目录形式存储数据
ops.set("user:01","lisi");
//添加多条数据
Map<String,String> map = new HashMap<>();
map.put("age","20");
map.put("address","sh");
ops.multiSet(map);
//获取多条数据
List<String> keys = new ArrayList<>();
keys.add("name");
keys.add("age");
keys.add("address");
List list = ops.multiGet(keys);
list.forEach(System.out::println);
//删除数据
redisTemplate.delete("name");
}
Hash类型操作
@Test
public void testHash(){
HashOperations hashOperations = redisTemplate.opsForHash();
/**
* 添加一条数据
* redis的key;hash的key;hash的value
*/
hashOperations.put("user","name","zhangsan");
/**
* 获取一条数据
* redis的key;hash的key
*/
hashOperations.get("user","name");
//添加多条数据
Map<String,String> map = new HashMap<>();
map.put("age","20");
map.put("address","sh");
hashOperations.putAll("user",map);
//获取多条数据
List<String> keys = new ArrayList<>();
keys.add("name");
keys.add("age");
keys.add("address");
List user = hashOperations.multiGet("user", keys);
user.forEach(System.out::println);
//获取hash类型的所有数据
Map<String,String> entries = hashOperations.entries("user");
entries.entrySet().forEach(e->{
System.out.println(e.getKey()+"-->"+e.getValue());
});
//hash的删除数据
hashOperations.delete("user","name","age");
}
List类型操作
@Test
public void testList(){
ListOperations listOperations = redisTemplate.opsForList();
//左添加
listOperations.leftPush("students","wangwu");
listOperations.leftPush("students","lisi");
/**
* 左添加
* redis的key;被左添加的数据;添加的数据,添加到第二个参数的左边
*/
listOperations.leftPush("students","wangwu","zhangsan");
//右添加
listOperations.rightPush("students","zhaoliu");
//获取数据
List list = listOperations.range("students", 0, 8);
list.forEach(System.out::println);
//获取总条数
Long size = listOperations.size("students");
System.out.println(size);
//删除数据
listOperations.remove("students",1,"lisi");
//左弹出
listOperations.leftPop("students");
//右弹出
listOperations.rightPop("students");
}
Set类型操作
@Test
public void testSet(){
SetOperations setOperations = redisTemplate.opsForSet();
//添加数据
String[] letters = new String[]{"aaa","bbb","ccc","ddd","eee"};
// setOperations.add("letters","aaa","bbb","ccc","ddd","eee");
setOperations.add("letters",letters);
//获取数据
Set set = setOperations.members("letters");
set.forEach(System.out::println);
//删除数据
setOperations.remove("letters","aaa","bbb");
}
ZSet类型操作
@Test
public void testSortedSet(){
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//添加数据
ZSetOperations.TypedTuple<Object> objectTypedTuple1 = new DefaultTypedTuple<>("zhangsan",7D);
ZSetOperations.TypedTuple<Object> objectTypedTuple2 = new DefaultTypedTuple<>("lisi",3D);
ZSetOperations.TypedTuple<Object> objectTypedTuple3 = new DefaultTypedTuple<>("wangwu",5D);
ZSetOperations.TypedTuple<Object> objectTypedTuple4 = new DefaultTypedTuple<>("zhaoliu",1D);
ZSetOperations.TypedTuple<Object> objectTypedTuple5 = new DefaultTypedTuple<>("tianqi",10D);
Set<ZSetOperations.TypedTuple<Object>> tuples = new HashSet<>();
tuples.add(objectTypedTuple1);
tuples.add(objectTypedTuple2);
tuples.add(objectTypedTuple3);
tuples.add(objectTypedTuple4);
tuples.add(objectTypedTuple5);
zSetOperations.add("score",tuples);
//获取数据
Set set = zSetOperations.range("score", 0, 4);
set.forEach(System.out::println);
//获取总条数
Long size = zSetOperations.size("score");
System.out.println(size);
zSetOperations.remove("score","zhangsan","lisi");
}
Spring Data Redis整合使用哨兵机制
application.yml配置
spring:
redis:
# Redis服务器地址
host: 127.0.0.1
# Redis服务器端口
port: 6379
# Redis认证密码
password: root
# Redis数据库
database: 0
# 连接超时时间
timeout: 10000ms
lettuce:
pool:
# 最大连接数,默认8
max-active: 1024
# 最大连接阻塞等待时间,单位毫秒,默认-1ms
max-wait: 10000ms
# 最大空闲连接,默认8
max-idle: 200
# 最小空闲连接,默认0
min-idle: 5
# 集群配置,哨兵模式
sentinel:
#主节点名称
master: mymaster
#节点
nodes: 127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381
Bean注解配置
@Configuration
public class RedisConfig {
@Bean
public RedisSentinelConfiguration redisSentinelConfiguration(){
RedisSentinelConfiguration redisSentinelConfiguration =
new RedisSentinelConfiguration()
//主节点名称
.master("mymaster")
//哨兵
.sentinel("127.0.0.1",6379)
.sentinel("127.0.0.1",6380)
.sentinel("127.0.0.1",6381);
//密码
redisSentinelConfiguration.setPassword("root");
return redisSentinelConfiguration;
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/137096.html