SpringDataRedis
官网:https://spring.io/projects/spring-data-redis
提供了对不同 Redis 客户端的整合(Lettuce 和 Jedis),默认是 Lettuce
提供了 RedisTemplate 统一 API 来操作 Redis
支持 Redis 的发布订阅模型
支持 Redis 哨兵和 Redis 集群
支持基于 Lettuce 的响应式编程
支持基于 JDK、JSON、字符串、Spring 对象的数据序列化及反序列化
支持基于 Redis 的 JDKCollection 实现
RedisTemplate
API | 返回值类型 | 说明 |
redisTemplate.opsForValue() | ValueOperations | 操作 String 类型数据 |
redisTemplate.opsForHash() | HashOperations | 操作 Hash 类型数据 |
redisTemplate.opsForList() | ListOperations | 操作 List 类型数据 |
redisTemplate.opsForSet() | SetOperations | 操作 Set 类型数据 |
redisTemplate.opsForZSet() | ZSetOperations | 操作 SortedSet 类型数据 |
redisTemplate | 通用的命令 |
使用步骤
引入 spring-boot-starter-data-redis 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在 application.yml 配置 Redis 的信息
spring:
redis:
host: 127.0.0.1
port: 6379
password: 123456
database: 0 # 几号库
lettuce:
pool:
max-active: 8 # 最大连接
max-idle: 8 # 最大空闲连接
min-idle: 0 # 最小空闲连接
max-wait: 100ms # 连接等待时间
注入 RedisTemplate
@Autowired
private RedisTemplate redisTemplate;
一、RedisTemplate各类型基本操作
package cn.com.easyExcel;
/**
* @ClassName: RedisDemo各类型操作
* @Author: ljy
* @Description:
* redisTemplate.opsForValue() ValueOperations 操作 String 类型数据
* redisTemplate.opsForHash() HashOperations 操作 Hash 类型数据
* redisTemplate.opsForList() ListOperations 操作 List 类型数据
* redisTemplate.opsForSet() SetOperations 操作 Set 类型数据
* redisTemplate.opsForZSet() ZSetOperations 操作 SortedSet 类型数据
*/
import cn.com.easyExcel.enums.SexEnum;
import cn.com.easyExcel.mapper.UserMapper;
import cn.com.easyExcel.pojo.User;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mysql.cj.util.StringUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
//RedisTemplate使用最详解(三)--- opsForHash()
// https://blog.csdn.net/weixin_43658899/article/details/121063660
@SpringBootTest
public class RedisDemo各类型操作 {
// 注入 RedisTemplate
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 注入 StringRedisTemplate
@Autowired
private StringRedisTemplate stringRedisTemplate;
/* 1、put(H var1, HK var2, HV var3)
新增hashMap值
var1 为Redis的key
var2 为key对应的map值的key
var3 为key对应的map值的值
var2相同替换var3*/
@Test
void put() {
redisTemplate.opsForHash().put("hashValue","map1","value1");
redisTemplate.opsForHash().put("hashValue","map2","value2");
redisTemplate.opsForHash().put("hashValue","map2","value3");
Map map = redisTemplate.opsForHash().entries("hashValue");
System.err.println(map);
//{map1=value1, map2=value3}
}
/* 2、get(H var1, Object var2)
获取key对应的map中,key为var2的map的对应的值*/
@Test
void get() {
Object o = redisTemplate.opsForHash().get("hashValue", "map1");
System.err.println("o = " + o);
//o = value1
}
/*3、entries(H key)
获取key对应的所有map键值对*/
@Test
void entries() {
Map hashValue = redisTemplate.opsForHash().entries("hashValue");
System.err.println("hashValue = " + hashValue);
//hashValue = {map1=value1, map2=value3}
}
/* 4、keys(H key)
获取key对应的map中所有的键*/
@Test
void keys() {
Set hashValue = redisTemplate.opsForHash().keys("hashValue");
System.err.println("hashValue = " + hashValue);
//hashValue = [map1, map2]
}
/* 5、values(H key)
获取key对应的map中所有的值*/
@Test
void values() {
List hashValue = redisTemplate.opsForHash().values("hashValue");
System.out.println("hashValue = " + hashValue);
//hashValue = [value1, value3]
}
/*6、hasKey(H key, Object var2)
判断key对应的map中是否有指定的键*/
@Test
void hasKey() {
Boolean aBoolean = redisTemplate.opsForHash().hasKey("hashValue", "map1");
System.out.println("aBoolean = " + aBoolean);
//aBoolean = true
/*7、size(H key)
获取key对应的map的长度*/
Long hashValue = redisTemplate.opsForHash().size("hashValue");
System.out.println("hashValue = " + hashValue);
//hashValue = 2
}
/*8、putIfAbsent(H key, HK var2, HV var3)
如果key对应的map不存在,则新增到map中,存在则不新增也不覆盖*/
@Test
void putIfAbsent() {
redisTemplate.opsForHash().putIfAbsent("hashValue", "map3", "value3");
}
/*9、putAll(H key, Map<? extends HK, ? extends HV> map)
直接以map集合的方式添加key对应的值
map中key已经存在,覆盖替换
map中key不存在,新增*/
@Test
void putAll() {
Map newMap = new HashMap();
newMap.put("map4","map44");
newMap.put("map5","map55");
redisTemplate.opsForHash().putAll("hashValue",newMap);
}
/*10、multiGet(H key, Collection var2)
以集合的方式获取这些键对应的map*/
@Test
void multiGet() {
List list = new ArrayList<>();
list.add("map1");
list.add("map2");
List hashValue = redisTemplate.opsForHash().multiGet("hashValue", list);
System.out.println("hashValue = " + hashValue);
//hashValue = [value1, value3]
}
/*11、lengthOfValue(H key, HK var2)
获取指定key对应的map集合中,指定键对应的值的长度*/
@Test
void lengthOfValue() {
Long aLong = redisTemplate.opsForHash().lengthOfValue("hashValue", "map1");
System.out.println("aLong = " + aLong);
//aLong = 6
}
/*12、increment(H key, HK var2, long long1)
使key对应的map中,键var2对应的值以long1自增*/
@Test
void increment() {
Long increment = redisTemplate.opsForHash().increment("hashValue", "map7", 1);
System.err.println("increment = " + increment);
//increment = 1
/*13、increment(H key, HK var2, double d1)
使key对应的map中,键var2对应的值以double类型d1自增*/
Double increment1 = redisTemplate.opsForHash().increment("hashValue", "map8", 1.2);
System.err.println("increment = " + increment1);
}
/*14、scan(H var1, ScanOptions var2)
匹配获取键值对
ScanOptions.NONE为获取全部键对
ScanOptions.scanOptions().match(“map1”).build(),匹配获取键位map1的键值对,不能模糊匹配*/
@Test
void scan() {
Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan("hashValue", ScanOptions.scanOptions().match("map1").build());
//Cursor<Map.Entry<Object,Object>> cursor = redisTemplate.opsForHash().scan("hashValue",ScanOptions.NONE);
while (cursor.hasNext()) {
Map.Entry<Object, Object> entry = cursor.next();
System.out.println("entry.getKey() = " + entry.getKey());
System.out.println("entry.getValue() = " + entry.getValue());
}
}
/* 15、delete(H key, Object… var2)
删除key对应的map中的键值对*/
@Test
void deleted () {
Long delete = redisTemplate.opsForHash().delete("hashValue", "map1", "map2");
System.out.println("delete = " + delete);
//map1和map2被删除
}
/*16、拓展
map中存储对象、对象集合时最好转为JSON字符串,容易解析
map中键值对都可以存为对象、对象集合JSON字符串,具体看实际应用存储*/
@Autowired
private UserMapper userMapper;
@Test
void toJSONString() {
//查询用户名包含a,年龄在20到30之间,邮箱信息不为null的用户信息 会排除逻辑删除项
//SELECT id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0
// AND (user_name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.between("age", 1, 30)
.isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
redisTemplate.opsForHash().put("hashValue", JSON.toJSONString(list), JSON.toJSONString(list));
}
@Test
void setSaveUsertoJSONString() {
User user = new User(16L, "小苗", 99, "99QQ", SexEnum.FEMALE);
// 序列化
stringRedisTemplate.opsForValue().set("user99", JSON.toJSONString(user));
String user1 = stringRedisTemplate.opsForValue().get("user99");
// 反序列化
User user2 = JSON.parseObject(user1, User.class);
System.out.println(user2);
//User(id=16, name=小苗, age=99, email=99QQ, sex=null, isDeleted=1)
}
}
二、RedisTemplate各类型基本操作+赋值
@SpringBootTest
public class RedisDemo各类型赋值 {
// 注入 RedisTemplate
@Autowired
private RedisTemplate redisTemplate;
// 注入 StringRedisTemplate
@Autowired
private StringRedisTemplate stringRedisTemplate;
// String 类型
@Test
void testString () {
redisTemplate.opsForValue().set("name", "xiaobai");
Object name = redisTemplate.opsForValue().get("name");
System.err.println(name);
//xiaobai
}
// Hash 类型
@Test
public void testHash () {
Set<String> sets = new HashSet<>();
sets.add("张三");
sets.add("李四");
sets.add("王五");
sets.add("赵六");
System.out.println(sets);
redisTemplate.opsForHash().put("user1", "name", "clarence");
redisTemplate.opsForHash().put("user1", "age", "25");
redisTemplate.opsForHash().put("user1", "age", "28");
redisTemplate.opsForHash().put("user1", "set", sets);
Map map = redisTemplate.opsForHash().entries("user1");
System.err.println(map);
//{name=clarence, age=28}
//具体参考各类型操作
boolean a = redisTemplate.opsForHash().putIfAbsent("user8", "name", sets);
redisTemplate.opsForHash().put("user1", "name", sets);
//设置hash类型过期时间 只能设置 key 不设置 hashKey
boolean c = redisTemplate.expire("user1", 20, TimeUnit.SECONDS);
}
// List 类型
@Test
public void testList () {
redisTemplate.opsForList().leftPushAll("names", "xiaobai", "xiaohei", "xiaolan");
List<String> names = redisTemplate.opsForList().range("names", 0, 3);
System.err.println(names);
//[xiaolan, xiaohei, xiaobai]
}
// Set 类型
@Test
public void testSet () {
redisTemplate.opsForSet().add("set", "a", "b", "c");
Set<String> set = redisTemplate.opsForSet().members("set");
System.err.println(set);
//[c, b, a]
}
// SortedSet 类型
@Test
public void testSortedSet () {
redisTemplate.opsForZSet().add("class", "xiaobai", 90);
Set aClass = redisTemplate.opsForZSet().rangeByScore("class", 90, 100);
System.err.println(aClass);
//[xiaobai]
Set<ZSetOperations.TypedTuple<String>> set = new HashSet<>();
set.add(new DefaultTypedTuple<>("xiaohei", 88.0));
set.add(new DefaultTypedTuple<>("xiaohui", 94.0));
set.add(new DefaultTypedTuple<>("xiaolan", 84.0));
set.add(new DefaultTypedTuple<>("xiaolv", 82.0));
set.add(new DefaultTypedTuple<>("xiaohong", 99.0));
redisTemplate.opsForZSet().add("class", set);
Set aClass1 = redisTemplate.opsForZSet().range("class", 0, 6);
System.err.println(aClass1);
//[xiaolv, xiaolan, xiaohei, xiaobai, xiaohui, xiaohong]
}
@Test
public void setTimeout() {
//向redis里存入数据和设置缓存时间
stringRedisTemplate.opsForValue().set("baike", "100", 60 * 10, TimeUnit.SECONDS);
//向redis里存入数据和设置缓存时间 ,10秒就过期了,过期就会自动删除
stringRedisTemplate.opsForValue().set("test", "100", 10, TimeUnit.SECONDS);
//val做-1操作
Long baike = stringRedisTemplate.boundValueOps("baike").increment(-1);
//根据key获取缓存中的val
String baike1 = stringRedisTemplate.opsForValue().get("baike");
//val +1
Long baike2 = stringRedisTemplate.boundValueOps("baike").increment(1);
//根据key获取过期时间
Long baike3 = stringRedisTemplate.getExpire("baike");
//根据key获取过期时间并换算成指定单位
Long baike4 = stringRedisTemplate.getExpire("baike", TimeUnit.SECONDS);
//根据key删除缓存
Boolean baike5 = stringRedisTemplate.delete("baike");
//检查key是否存在,返回boolean值
Boolean aBoolean = stringRedisTemplate.hasKey("baike");
//向指定key中存放set集合
stringRedisTemplate.opsForSet().add("baike", "1", "2", "3");
//设置过期时间
Boolean aBoolean1 = stringRedisTemplate.expire("baike", 1000, TimeUnit.MILLISECONDS);
//根据key查看集合中是否存在指定数据
Boolean member = stringRedisTemplate.opsForSet().isMember("baike", "1");
//根据key获取set集合
Set<String> set = stringRedisTemplate.opsForSet().members("baike");
//验证有效时间
Long expire = redisTemplate.boundHashOps("baike").getExpire();
/**
* 从redis中获取key对应的过期时间;
* 如果该值有过期时间,就返回相应的过期时间;
* 如果该值没有设置过期时间,就返回-1;
* 如果没有该值,就返回-2;
*/
Long key的名称 = redisTemplate.opsForValue().getOperations().getExpire("key的名称");
}
}
三、分布式锁的实现
/**
* 17、分布式锁的实现:解决的问题就是需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题
*/
@Test
public void test() {
redisTemplate.opsForValue().set("num", 0);
}
@Test
void setLock(){
// UUID防止误删
String uuid=String.valueOf(UUID.randomUUID());
//1获取锁,setne
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,20, TimeUnit.SECONDS);
//2获取锁成功,查询num的值
if (lock) {
Object value = redisTemplate.opsForValue().get("num");
//2.1判断num为空return
if (Objects.isNull(value) ) {
return;
}
//2.2有值就转成int
int num = Integer.parseInt(value + "");
//2.3吧Redis的num加1
redisTemplate.opsForValue().set("num", ++num);
//2.4释放锁,del
String lockuuid = (String) redisTemplate.opsForValue().get("lock");
if(uuid.equals(lockuuid)){
redisTemplate.delete("lock");
}
} else {
//获取锁失败,每隔0.1秒再获取
try {
Thread.sleep(100);
setLock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}